1

我正在尝试在 Windows 机器上创建一个自签名证书。我希望证书是可导出的(甚至是私钥)。但是,我在完成这项任务时遇到了一些困难。

我发现了一些指向 WIN32 调用的网站。当我在 Windows XP 上运行代码时,证书存储在个人证书中,可以很好地导出。当我在 Windows 7 64 位上运行此代码时,我得到的证书没有错误,但证书不可导出。然后我不能将它用于我分配证书的网站。

        Certificate.Check(Certificate.NativeMethods.CryptAcquireContextW(
                out providerContext,
                containerName,
                null,
                1, // PROV_RSA_FULL
                8)); // CRYPT_NEWKEYSET

            Certificate.Check(Certificate.NativeMethods.CryptGenKey(
                providerContext,
                1, // AT_KEYEXCHANGE
                1, // CRYPT_EXPORTABLE
                out cryptKey));

            IntPtr errorStringPtr;
            int nameDataLength = 0;
            byte[] nameData;

            // errorStringPtr gets a pointer into the middle of the x500 string,
            // so x500 needs to be pinned until after we've copied the value
            // of errorStringPtr.
            dataHandle = GCHandle.Alloc(commonName, GCHandleType.Pinned);

            if (!Certificate.NativeMethods.CertStrToNameW(
                0x00000001, // X509_ASN_ENCODING
                dataHandle.AddrOfPinnedObject(),
                3, // CERT_X500_NAME_STR = 3
                IntPtr.Zero,
                null,
                ref nameDataLength,
                out errorStringPtr))
            {
                string error = Marshal.PtrToStringUni(errorStringPtr);
                throw new ArgumentException(error);
            }

            nameData = new byte[nameDataLength];

            if (!Certificate.NativeMethods.CertStrToNameW(
                0x00000001, // X509_ASN_ENCODING
                dataHandle.AddrOfPinnedObject(),
                3, // CERT_X500_NAME_STR = 3
                IntPtr.Zero,
                nameData,
                ref nameDataLength,
                out errorStringPtr))
            {
                string error = Marshal.PtrToStringUni(errorStringPtr);
                throw new ArgumentException(error);
            }
            Console.WriteLine("THIS IS CHANGED");

            dataHandle.Free();

            dataHandle = GCHandle.Alloc(nameData, GCHandleType.Pinned);
            Certificate.CryptoApiBlob nameBlob = new Certificate.CryptoApiBlob(
                nameData.Length,
                dataHandle.AddrOfPinnedObject());

            Certificate.CryptKeyProviderInformation kpi = new Certificate.CryptKeyProviderInformation();
            kpi.ContainerName = containerName;
            kpi.ProviderType = 1; // PROV_RSA_FULL
            kpi.KeySpec = 1; // AT_KEYEXCHANGE

            certContext = Certificate.NativeMethods.CertCreateSelfSignCertificate(
                IntPtr.Zero,
                ref nameBlob,
                0,
                ref kpi,
                IntPtr.Zero, // default = SHA1RSA
                ref startSystemTime,
                ref endSystemTime,
                IntPtr.Zero);
            Certificate.Check(certContext != IntPtr.Zero);
            dataHandle.Free();

            X509Certificate2 tempCert = new X509Certificate2(certContext);
            //result = new X509Certificate2(tempCert.RawData, "", X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet);
            result = tempCert;

            X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
            store.Open(OpenFlags.ReadWrite);
            store.Add(result);
            store.Close();

请注意,Certificate 类是一个内部类,它只包含我正在使用的不同静态方法和 WIN32 定义。这是 NativeMethods 类定义(显示了我正在使用的 WIN32 API 调用):

    internal static class NativeMethods
{
    [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool FileTimeToSystemTime(
        [In] ref long fileTime,
        out SystemTime systemTime);

    [DllImport("AdvApi32.dll", SetLastError = true, ExactSpelling = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool CryptAcquireContextW(
        out IntPtr providerContext,
        [MarshalAs(UnmanagedType.LPWStr)] string container,
        [MarshalAs(UnmanagedType.LPWStr)] string provider,
        int providerType,
        int flags);

    [DllImport("AdvApi32.dll", SetLastError = true, ExactSpelling = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool CryptReleaseContext(
        IntPtr providerContext,
        int flags);

    [DllImport("AdvApi32.dll", SetLastError = true, ExactSpelling = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool CryptGenKey(
        IntPtr providerContext,
        int algorithmId,
        int flags,
        out IntPtr cryptKeyHandle);

    [DllImport("AdvApi32.dll", SetLastError = true, ExactSpelling = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool CryptDestroyKey(
        IntPtr cryptKeyHandle);

    [DllImport("Crypt32.dll", SetLastError = true, ExactSpelling = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool CertStrToNameW(
        int certificateEncodingType,
        IntPtr x500,
        int strType,
        IntPtr reserved,
        [MarshalAs(UnmanagedType.LPArray)] [Out] byte[] encoded,
        ref int encodedLength,
        out IntPtr errorString);

    [DllImport("Crypt32.dll", SetLastError = true, ExactSpelling = true)]
    public static extern IntPtr CertCreateSelfSignCertificate(
        IntPtr providerHandle,
        [In] ref CryptoApiBlob subjectIssuerBlob,
        int flags,
        [In] ref CryptKeyProviderInformation keyProviderInformation,
        IntPtr signatureAlgorithm,
        [In] ref SystemTime startTime,
        [In] ref SystemTime endTime,
        IntPtr extensions);

    [DllImport("Crypt32.dll", SetLastError = true, ExactSpelling = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool CertFreeCertificateContext(
        IntPtr certificateContext);

    [DllImport("Crypt32.dll", SetLastError = true, ExactSpelling = true)]
    public static extern IntPtr CertOpenStore(
        [MarshalAs(UnmanagedType.LPStr)] string storeProvider,
        int messageAndCertificateEncodingType,
        IntPtr cryptProvHandle,
        int flags,
        IntPtr parameters);

    [DllImport("Crypt32.dll", SetLastError = true, ExactSpelling = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool CertCloseStore(
        IntPtr certificateStoreHandle,
        int flags);

    [DllImport("Crypt32.dll", SetLastError = true, ExactSpelling = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool CertAddCertificateContextToStore(
        IntPtr certificateStoreHandle,
        IntPtr certificateContext,
        int addDisposition,
        out IntPtr storeContextPtr);

    [DllImport("Crypt32.dll", SetLastError = true, ExactSpelling = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool CertSetCertificateContextProperty(
        IntPtr certificateContext,
        int propertyId,
        int flags,
        [In] ref CryptKeyProviderInformation data);

    [DllImport("Crypt32.dll", SetLastError = true, ExactSpelling = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool PFXExportCertStoreEx(
        IntPtr certificateStoreHandle,
        ref CryptoApiBlob pfxBlob,
        IntPtr password,
        IntPtr reserved,
        int flags);
}

我是在 32 位还是 64 位机器上重要吗?在这一点上,我不知道该怎么做。我从以下链接获得此代码:在 C# 中创建自签名证书

4

1 回答 1

3

我发现了导致我无法导出证书的问题。事实证明,我实际上也无法在 Windows XP 上导出它(我见过几次,但那是在我调试的时候,后来没有执行导致它崩溃的代码)。

在清理例程中,密钥集被删除:

NativeMethods.CryptAcquireContextW(
    out providerContext,
    containerName,
    null,
    1, // PROV_RSA_FULL
    0x10); // CRYPT_DELETEKEYSET

这会导致证书不再能够导出私钥。当此行被注释掉时,证书已安装在本地计算机的个人证书存储中。它也被允许导出,因此允许我将 IIS 配置为指向该证书。

于 2012-06-29T17:50:28.543 回答