创建可导出的自签名证书

时间:2012-06-29 15:22:15

标签: .net security winapi x509certificate2

我正在尝试在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位机器上,这有关系吗?我不知道此时该做些什么。我从以下链接获得此代码:Creating Self Signed Cert in C#

1 个答案:

答案 0 :(得分:3)

我发现问题导致我无法导出证书。事实证明,我实际上无法在Windows XP上导出它(我已经看过几次了,但那时我正在调试并且之后没有执行代码导致它中断)。

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

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

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