简体   繁体   中英

Create Self Signed Certificate that is Exportable

I am trying to create a self signed certificate on Windows machines. I want the certificate be be exportable (even the private keys). However, I am having some difficulty in accomplishing this task.

I found some web sites that point to the WIN32 calls. When I run the code on Windows XP, the certificate is stored in the Personal certificates and can be exported just fine. When I run this code on Windows 7 64-bit, I am getting the certificate an no errors, but the certificate is not exportable. I cannot then use it for the website I am assigning the certificate to.

        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();

Note the Certificate class is an internal class that just wraps around the different static methods and WIN32 definitions that I am using. Here is the NativeMethods class definition (which shows the WIN32 API calls I am using):

    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);
}

Does it matter if I am on a 32-bit or 64-bit machine? I am at a loss as to what to do at this point. I got this code from the following link: Creating Self Signed Cert in C#

I found the problem that was causing me to not be able to export the certificate. It turns out that I was not actually able to export it on Windows XP either (I had seen it a few times but that was when I was debugging and had not executed code later on that caused it to break).

In a clean up routine, the key set was getting deleted:

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

This causes the certificate to no longer be able to export the private key. When this line was commented out, the certificate was installed in the Personal Certificates store of the Local Computer. It was also allowed to be exported and therefore allowed me to configure IIS to point to this certificate.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM