【问题标题】:How to link KSP DLL to Certificate如何将 KSP DLL 链接到证书
【发布时间】:2022-08-17 19:36:12
【问题描述】:

我想使用私钥在本地系统上不可用的证书对 HLKX 文件进行签名。

我根据“加密提供程序开发工具包”中的 KSP DLL 的代码示例创建了一个自定义密钥存储提供程序(基本上是一个用于测试目的的外壳),我可以注册它,它显示在枚举中系统上可用的 KSP。

我正在使用以下示例所示的符号函数: https://docs.microsoft.com/en-us/windows-hardware/test/hlk/user/hlk-signing-with-an-hsm 在 C# 应用程序中。

自定义 KSP dll 应该处理所有签名命令并连接到允许使用私钥的后端,该私钥存储在 HSM 中的附加软件层后面,该软件层限制某些用户的密钥访问。

当我运行应用程序时,由于缺少私钥,签名失败。因此,我需要以某种方式将证书(文件中的证书或导入系统的证书存储)链接到 KSP,从而导致对签名哈希等的调用最终出现在 KSP 的 API 中,但我找不到任何合适的信息来说明如何: a) 将对 KSP 的引用添加到 C# 签名调用 或者 b) 将证书导入到证书存储区并引用 KSP,以便在证书用于签名时自动使用它。

那么,我该怎么做a)或b)或者有什么其他方式来手动处理这个?签名应用程序只是使用 C#,因为这是我可以从 Microsoft 找到的用于此用例的唯一示例。如果在 C/C++ 中有一个示例,那也很好。我想在使用 CSP 而不是 KSP 的情况下问题将是相同的,但不幸的是,许多帖子将两者大量混合。

标签: microsoft-key-storage-provider


【解决方案1】:

我找到了一种在商店中的证书和提供者 dll 之间创建链接的方法(通过提供者 dll 在系统中注册的名称)。相关的系统API函数是Crypt32.dll中的CertSetCertificateContextProperty和CertGetCertificateContextProperty。我能够验证这是否适用于签署 hlkx 文件(使用 Hardware Lab Kit 软件或通过使用 PackageDigitalSignatureManager 的 C# 代码),但我仍然在使用这种方式进行签名时遇到问题,例如使用 Microsoft 的 signtool.exe 的可执行文件抱怨私钥不可用于证书。

我在 C# 中使用系统 API 函数,因此我从我的项目中提取了有关如何将证书与提供者链接以及如何从证书中读取链接提供者信息的相关代码片段。

    class Program
{
    private const UInt32 CERT_SET_KEY_CONTEXT_PROP_ID = 0x00000001;
    private const UInt32 CERT_SET_KEY_PROV_HANDLE_PROP_ID = 0x00000001;
    private const UInt32 CERT_KEY_PROV_INFO_PROP_ID = 2;

    static void Main(string[] args)
    {
        // Reading certificate from file
        X509Certificate2 certificate = new X509Certificate2("C:\\MyCert.crt");

        // Adding certificate to store
        X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
        store.Open(OpenFlags.ReadWrite);
        store.Add(certificate);
        store.Close();

        // Linking certificate with provider
        // ProviderName is the name under which the provider is registered in the system
        // ContainerName is a string that will be passed to the DLL when calls are made it can be used to
        // additional information to the DLL that can be set when linking the certificate with the provider
        SetCertificateProviderInformation("My Provider Name", "MyContainerName", certificate);

        // Read provider information
        GetCertificateProviderInformation(certificate);
    }

    private static void SetCertificateProviderInformation(string providerName, string containerName, X509Certificate2 certificate)
    {
        Crypt32Dll.CRYPT_KEY_PROV_INFO cryptKeyProvInfo = new Crypt32Dll.CRYPT_KEY_PROV_INFO
        {
            pwszProvName = providerName,
            pwszContainerName = containerName,
            dwProvType = 24,
            dwFlags = CERT_SET_KEY_CONTEXT_PROP_ID | CERT_SET_KEY_PROV_HANDLE_PROP_ID,
            cProvParam = 0,
            rgProvParam = IntPtr.Zero,
            dwKeySpec = 2
        };

        IntPtr pvData = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Crypt32Dll.CRYPT_KEY_PROV_INFO)));
        Marshal.StructureToPtr(cryptKeyProvInfo, pvData, false);

        if (Crypt32Dll.CertSetCertificateContextProperty(certificate.Handle, CERT_KEY_PROV_INFO_PROP_ID, 0, pvData))
        {
            // succeeded
        }
        else
        {
            Int32 lastError = Marshal.GetLastWin32Error();
            // failed
        }


        if (pvData != IntPtr.Zero)
        {
            Marshal.FreeHGlobal(pvData);
        }
    }

    private static void GetCertificateProviderInformation(X509Certificate2 certificate)
    {
        UInt32 dataSize = 0;

        // Get required size for struct
        if (Crypt32Dll.CertGetCertificateContextProperty(certificate.Handle, CERT_KEY_PROV_INFO_PROP_ID, IntPtr.Zero, ref dataSize))
        {
            // Allocate unmanaged struct memory of required size and query the information
            IntPtr pvData = Marshal.AllocHGlobal((int)dataSize);
            if (Crypt32Dll.CertGetCertificateContextProperty(certificate.Handle, CERT_KEY_PROV_INFO_PROP_ID, pvData, ref dataSize))
            {
                // succeeded
                Crypt32Dll.CRYPT_KEY_PROV_INFO keyProviderInformation = (Crypt32Dll.CRYPT_KEY_PROV_INFO)Marshal.PtrToStructure(pvData, typeof(Crypt32Dll.CRYPT_KEY_PROV_INFO));
                Console.Out.WriteLine("Provider Name: " + keyProviderInformation.pwszProvName);
                Console.Out.WriteLine("Container Name: "  + keyProviderInformation.pwszContainerName);
            }
            else
            {
                int lastError = Marshal.GetLastWin32Error();
                // failed
            }

            // Free unmanaged struct memory
            Marshal.FreeHGlobal(pvData);
        }
        else
        {
            // failed
        }
    }
}

使用 Crypt32.dll 的代码为:

    class Crypt32Dll
{
    private const string DLL_NAME = "Crypt32.dll";

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    internal struct CRYPT_KEY_PROV_INFO
    {
        [MarshalAs(UnmanagedType.LPWStr)]
        internal string pwszContainerName;
        [MarshalAs(UnmanagedType.LPWStr)]
        internal string pwszProvName;
        internal UInt32 dwProvType;
        internal UInt32 dwFlags;
        internal UInt32 cProvParam;
        internal IntPtr rgProvParam;
        internal UInt32 dwKeySpec;
    }

    [DllImport(DLL_NAME, EntryPoint = "CertSetCertificateContextProperty", CharSet = CharSet.Auto, SetLastError = true)]
    internal static extern bool CertSetCertificateContextProperty(
        IntPtr pCertContext,
        UInt32 dwPropId,
        UInt32 dwFlags,
        IntPtr pvData
        );

    [DllImport(DLL_NAME, EntryPoint = "CertGetCertificateContextProperty", CharSet = CharSet.Auto, SetLastError = true)]
    internal static extern bool CertGetCertificateContextProperty(
        IntPtr pCertContext,
        UInt32 dwPropId,
        IntPtr pvData,
        ref UInt32 pcbData
        );
}

【讨论】:

    猜你喜欢
    • 2019-01-02
    • 1970-01-01
    • 2011-04-29
    • 1970-01-01
    • 2013-09-18
    • 2015-08-24
    • 2017-06-11
    • 1970-01-01
    • 2023-01-26
    相关资源
    最近更新 更多