Example #1
0
        static unsafe int Main()
        {
            HRESULT hr = HRESULT.S_OK;

            IntPtr[] rgParaEKU = { szOID_PKIX_KP_EMAIL_PROTECTION };

            var EKUCriteria = new CERT_SELECT_CRITERIA
            {
                dwType = CertSelectBy.CERT_SELECT_BY_ENHKEY_USAGE,
                cPara  = (uint)rgParaEKU.Length,
            };

            fixed(IntPtr *pArr = rgParaEKU)
            EKUCriteria.ppPara = (IntPtr)(void *)pArr;


            var bDigSig   = (byte)CertKeyUsage.CERT_DIGITAL_SIGNATURE_KEY_USAGE;           // in byte 0
            var pDigSig   = &bDigSig;
            var extDigSig = new CERT_EXTENSION
            {
                Value = new CRYPTOAPI_BLOB {
                    cbData = 1, pbData = (IntPtr)pDigSig
                }
            };

            using var pExtDigSig = SafeCoTaskMemHandle.CreateFromStructure(extDigSig);
            IntPtr[] rgParaKU = { pExtDigSig };

            var KUCriteria = new CERT_SELECT_CRITERIA
            {
                dwType = CertSelectBy.CERT_SELECT_BY_KEY_USAGE,
                cPara  = (uint)rgParaKU.Length
            };

            fixed(IntPtr *pArr = rgParaKU)
            KUCriteria.ppPara = (IntPtr)(void *)pArr;

            CERT_SELECT_CRITERIA[] rgCriteriaFilter = { EKUCriteria, KUCriteria };

            using var hStore = CertOpenStore(CertStoreProvider.CERT_STORE_PROV_SYSTEM, 0, default,
                                             CertStoreFlags.CERT_SYSTEM_STORE_CURRENT_USER, "MY");

            if (hStore.IsInvalid)
            {
                hr = (HRESULT)Win32Error.GetLastError();
                goto CleanUp;
            }

            Console.Write("Looking for certificates in MY store ...\n");

            if (!CertSelectCertificateChains(default, CertSelection.CERT_SELECT_TRUSTED_ROOT | CertSelection.CERT_SELECT_HAS_PRIVATE_KEY, default,
        public MarshalX509Extension(X509Extension extension)
        {
            _extension = extension;
            _blobPtr   = Marshal.AllocHGlobal(extension.RawData.Length);
            Marshal.Copy(extension.RawData, 0, _blobPtr, extension.RawData.Length);
            var blob = new CRYPT_OBJID_BLOB();

            blob.cbData = (uint)extension.RawData.Length;
            blob.pbData = _blobPtr;
            var nativeExtension = new CERT_EXTENSION();

            nativeExtension.fCritical = extension.Critical;
            nativeExtension.pszObjId  = extension.Oid.Value;
            nativeExtension.Value     = blob;
            _value = nativeExtension;
        }
 public SafeX509Extension(X509Extension extension)
 {
     this.blobPtr = Marshal.AllocHGlobal(extension.RawData.Length);
     Marshal.Copy(extension.RawData, 0, this.blobPtr, extension.RawData.Length);
     var blob = new CRYPTOAPI_BLOB
     {
         cbData = (uint)extension.RawData.Length,
         pbData = this.blobPtr
     };
     var nativeExtension = new CERT_EXTENSION
     {
         fCritical = extension.Critical,
         pszObjId = extension.Oid.Value,
         Value = blob
     };
     this.value = nativeExtension;
 }
        public SafeX509Extension(X509Extension extension)
        {
            this.blobPtr = Marshal.AllocHGlobal(extension.RawData.Length);
            Marshal.Copy(extension.RawData, 0, this.blobPtr, extension.RawData.Length);
            var blob = new CRYPTOAPI_BLOB
            {
                cbData = (uint)extension.RawData.Length,
                pbData = this.blobPtr
            };
            var nativeExtension = new CERT_EXTENSION
            {
                fCritical = extension.Critical,
                pszObjId  = extension.Oid.Value,
                Value     = blob
            };

            this.value = nativeExtension;
        }
Example #5
0
        internal static SafeLocalAllocHandle DecodeExtension(CERT_EXTENSION extension)
        {
            SafeLocalAllocHandle decodedExtension = null;
            int decodedSize = 0;

            bool decoded = UnsafeNativeMethods.CryptDecodeObjectEx(CertificateEncodingType.Pkcs7AsnEncoding | CertificateEncodingType.X509AsnEncoding,
                                                                   extension.pszObjId,
                                                                   extension.Value.pbData,
                                                                   extension.Value.cbData,
                                                                   DecodeObjectFlags.AllocateMemory | DecodeObjectFlags.NoCopy | DecodeObjectFlags.ShareOidStrings,
                                                                   IntPtr.Zero,
                                                                   out decodedExtension,
                                                                   ref decodedSize);

            if (!decoded)
            {
                Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
            }

            return(decodedExtension);
        }
Example #6
0
            public CERT_EXTENSIONS(CERT_EXTENSION[] extensions)
            {
                this.cExtension = (uint)extensions.Length;

                if (extensions.Length > 0)
                {
                    IntPtr buf = Marshal.AllocHGlobal(extensions.Length * Marshal.SizeOf(typeof(CERT_EXTENSION)));
                    this.rgExtension = buf;

                    for (int i = 0; i < extensions.Length; i++)
                    {
                        Marshal.StructureToPtr(extensions[i], buf, false);
                        buf += Marshal.SizeOf(typeof(CERT_EXTENSION));
                    }
                }
            }
Example #7
0
        internal static SafeCertificateContextHandle CreateSelfSignedCertificate(SafeNCryptKeyHandle key,
                                                                                 byte[] subjectName,
                                                                                 X509CertificateCreationOptions creationOptions,
                                                                                 string signatureAlgorithmOid,
                                                                                 DateTime startTime,
                                                                                 DateTime endTime,
                                                                                 X509ExtensionCollection extensions)
        {
            Debug.Assert(key != null, "key != null");
            Debug.Assert(!key.IsClosed && !key.IsInvalid, "!key.IsClosed && !key.IsInvalid");
            Debug.Assert(subjectName != null, "subjectName != null");
            Debug.Assert(!String.IsNullOrEmpty(signatureAlgorithmOid), "!String.IsNullOrEmpty(signatureAlgorithmOid)");
            Debug.Assert(extensions != null, "extensions != null");

            // Create an algorithm identifier structure for the signature algorithm
            CapiNative.CRYPT_ALGORITHM_IDENTIFIER nativeSignatureAlgorithm = new CapiNative.CRYPT_ALGORITHM_IDENTIFIER();
            nativeSignatureAlgorithm.pszObjId          = signatureAlgorithmOid;
            nativeSignatureAlgorithm.Parameters        = new CapiNative.CRYPTOAPI_BLOB();
            nativeSignatureAlgorithm.Parameters.cbData = 0;
            nativeSignatureAlgorithm.Parameters.pbData = IntPtr.Zero;

            // Convert the begin and expire dates to system time structures
            Win32Native.SYSTEMTIME nativeStartTime = new Win32Native.SYSTEMTIME(startTime);
            Win32Native.SYSTEMTIME nativeEndTime   = new Win32Native.SYSTEMTIME(endTime);

            // Map the extensions into CERT_EXTENSIONS.  This involves several steps to get the
            // CERT_EXTENSIONS ready for interop with the native APIs.
            //   1. Build up the CERT_EXTENSIONS structure in managed code
            //   2. For each extension, create a managed CERT_EXTENSION structure; this requires allocating
            //      native memory for the blob pointer in the CERT_EXTENSION. These extensions are stored in
            //      the nativeExtensionArray variable.
            //   3. Get a block of native memory that can hold a native array of CERT_EXTENSION structures.
            //      This is the block referenced by the CERT_EXTENSIONS structure.
            //   4. For each of the extension structures created in step 2, marshal the extension into the
            //      native buffer allocated in step 3.
            CERT_EXTENSIONS nativeExtensions = new CERT_EXTENSIONS();

            nativeExtensions.cExtension = extensions.Count;
            CERT_EXTENSION[] nativeExtensionArray = new CERT_EXTENSION[extensions.Count];

            // Run this in a CER to ensure that we release any native memory allocated for the certificate
            // extensions.
            RuntimeHelpers.PrepareConstrainedRegions();
            try
            {
                // Copy over each extension into a native extension structure, including allocating native
                // memory for its blob if necessary.
                for (int i = 0; i < extensions.Count; ++i)
                {
                    nativeExtensionArray[i]           = new CERT_EXTENSION();
                    nativeExtensionArray[i].pszObjId  = extensions[i].Oid.Value;
                    nativeExtensionArray[i].fCritical = extensions[i].Critical;

                    nativeExtensionArray[i].Value        = new CapiNative.CRYPTOAPI_BLOB();
                    nativeExtensionArray[i].Value.cbData = extensions[i].RawData.Length;
                    if (nativeExtensionArray[i].Value.cbData > 0)
                    {
                        nativeExtensionArray[i].Value.pbData =
                            Marshal.AllocCoTaskMem(nativeExtensionArray[i].Value.cbData);
                        Marshal.Copy(extensions[i].RawData,
                                     0,
                                     nativeExtensionArray[i].Value.pbData,
                                     nativeExtensionArray[i].Value.cbData);
                    }
                }

                // Now that we've built up the extension array, create a block of native memory to marshal
                // them into.
                if (nativeExtensionArray.Length > 0)
                {
                    checked
                    {
                        // CERT_EXTENSION structures end with a pointer field, which means on all supported
                        // platforms they won't require any padding between elements of the array.
                        nativeExtensions.rgExtension =
                            Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(CERT_EXTENSION)) * nativeExtensionArray.Length);

                        for (int i = 0; i < nativeExtensionArray.Length; ++i)
                        {
                            ulong  offset            = (uint)i * (uint)Marshal.SizeOf(typeof(CERT_EXTENSION));
                            ulong  next              = offset + (ulong)nativeExtensions.rgExtension.ToInt64();
                            IntPtr nextExtensionAddr = new IntPtr((long)next);

                            Marshal.StructureToPtr(nativeExtensionArray[i], nextExtensionAddr, false);
                        }
                    }
                }

                //
                // Now that all of the needed data structures are setup, we can create the certificate
                //

                unsafe
                {
                    fixed(byte *pSubjectName = &subjectName[0])
                    {
                        // Create a CRYPTOAPI_BLOB for the subject of the cert
                        CapiNative.CRYPTOAPI_BLOB nativeSubjectName = new CapiNative.CRYPTOAPI_BLOB();
                        nativeSubjectName.cbData = subjectName.Length;
                        nativeSubjectName.pbData = new IntPtr(pSubjectName);

                        // Now that we've converted all the inputs to native data structures, we can generate
                        // the self signed certificate for the input key.
                        SafeCertificateContextHandle selfSignedCertHandle =
                            UnsafeNativeMethods.CertCreateSelfSignCertificate(key,
                                                                              ref nativeSubjectName,
                                                                              creationOptions,
                                                                              IntPtr.Zero,
                                                                              ref nativeSignatureAlgorithm,
                                                                              ref nativeStartTime,
                                                                              ref nativeEndTime,
                                                                              ref nativeExtensions);

                        if (selfSignedCertHandle.IsInvalid)
                        {
                            throw new CryptographicException(Marshal.GetLastWin32Error());
                        }

                        return(selfSignedCertHandle);
                    }
                }
            }
            finally
            {
                //
                // In order to release all resources held by the CERT_EXTENSIONS we need to do three things
                //   1. Destroy each structure marshaled into the native CERT_EXTENSION array
                //   2. Release the memory used for the CERT_EXTENSION array
                //   3. Release the memory used in each individual CERT_EXTENSION
                //

                // Release each extension marshaled into the native buffer as well
                if (nativeExtensions.rgExtension != IntPtr.Zero)
                {
                    for (int i = 0; i < nativeExtensionArray.Length; ++i)
                    {
                        ulong  offset            = (uint)i * (uint)Marshal.SizeOf(typeof(CERT_EXTENSION));
                        ulong  next              = offset + (ulong)nativeExtensions.rgExtension.ToInt64();
                        IntPtr nextExtensionAddr = new IntPtr((long)next);

                        Marshal.DestroyStructure(nextExtensionAddr, typeof(CERT_EXTENSION));
                    }

                    Marshal.FreeCoTaskMem(nativeExtensions.rgExtension);
                }

                // If we allocated memory for any extensions, make sure to free it now
                for (int i = 0; i < nativeExtensionArray.Length; ++i)
                {
                    if (nativeExtensionArray[i].Value.pbData != IntPtr.Zero)
                    {
                        Marshal.FreeCoTaskMem(nativeExtensionArray[i].Value.pbData);
                    }
                }
            }
        }
Example #8
0
        internal static SafeCertContextHandle CreateSelfSignedCertificate(CngKey key,
                                                                          bool takeOwnershipOfKey,
                                                                          byte[] subjectName,
                                                                          X509CertificateCreationOptions creationOptions,
                                                                          string signatureAlgorithmOid,
                                                                          DateTime startTime,
                                                                          DateTime endTime,
                                                                          X509ExtensionCollection extensions)
        {
            Debug.Assert(key != null, "key != null");
            Debug.Assert(subjectName != null, "subjectName != null");
            Debug.Assert(!String.IsNullOrEmpty(signatureAlgorithmOid), "!String.IsNullOrEmpty(signatureAlgorithmOid)");
            Debug.Assert(extensions != null, "extensions != null");

            // Create an algorithm identifier structure for the signature algorithm
            CapiNative.CRYPT_ALGORITHM_IDENTIFIER nativeSignatureAlgorithm = new CapiNative.CRYPT_ALGORITHM_IDENTIFIER();
            nativeSignatureAlgorithm.pszObjId          = signatureAlgorithmOid;
            nativeSignatureAlgorithm.Parameters        = new CapiNative.CRYPTOAPI_BLOB();
            nativeSignatureAlgorithm.Parameters.cbData = 0;
            nativeSignatureAlgorithm.Parameters.pbData = IntPtr.Zero;

            // Convert the begin and expire dates to system time structures
            Win32Native.SYSTEMTIME nativeStartTime = new Win32Native.SYSTEMTIME(startTime);
            Win32Native.SYSTEMTIME nativeEndTime   = new Win32Native.SYSTEMTIME(endTime);

            // Map the extensions into CERT_EXTENSIONS.  This involves several steps to get the
            // CERT_EXTENSIONS ready for interop with the native APIs.
            //   1. Build up the CERT_EXTENSIONS structure in managed code
            //   2. For each extension, create a managed CERT_EXTENSION structure; this requires allocating
            //      native memory for the blob pointer in the CERT_EXTENSION. These extensions are stored in
            //      the nativeExtensionArray variable.
            //   3. Get a block of native memory that can hold a native array of CERT_EXTENSION structures.
            //      This is the block referenced by the CERT_EXTENSIONS structure.
            //   4. For each of the extension structures created in step 2, marshal the extension into the
            //      native buffer allocated in step 3.
            CERT_EXTENSIONS nativeExtensions = new CERT_EXTENSIONS();

            nativeExtensions.cExtension = extensions.Count;
            CERT_EXTENSION[] nativeExtensionArray = new CERT_EXTENSION[extensions.Count];

            // Run this in a CER to ensure that we release any native memory allocated for the certificate
            // extensions.
            RuntimeHelpers.PrepareConstrainedRegions();
            try
            {
                // Copy over each extension into a native extension structure, including allocating native
                // memory for its blob if necessary.
                for (int i = 0; i < extensions.Count; ++i)
                {
                    nativeExtensionArray[i]           = new CERT_EXTENSION();
                    nativeExtensionArray[i].pszObjId  = extensions[i].Oid.Value;
                    nativeExtensionArray[i].fCritical = extensions[i].Critical;

                    nativeExtensionArray[i].Value        = new CapiNative.CRYPTOAPI_BLOB();
                    nativeExtensionArray[i].Value.cbData = extensions[i].RawData.Length;
                    if (nativeExtensionArray[i].Value.cbData > 0)
                    {
                        nativeExtensionArray[i].Value.pbData =
                            Marshal.AllocCoTaskMem(nativeExtensionArray[i].Value.cbData);
                        Marshal.Copy(extensions[i].RawData,
                                     0,
                                     nativeExtensionArray[i].Value.pbData,
                                     nativeExtensionArray[i].Value.cbData);
                    }
                }

                // Now that we've built up the extension array, create a block of native memory to marshal
                // them into.
                if (nativeExtensionArray.Length > 0)
                {
                    checked
                    {
                        // CERT_EXTENSION structures end with a pointer field, which means on all supported
                        // platforms they won't require any padding between elements of the array.
                        nativeExtensions.rgExtension =
                            Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(CERT_EXTENSION)) * nativeExtensionArray.Length);

                        for (int i = 0; i < nativeExtensionArray.Length; ++i)
                        {
                            ulong  offset            = (uint)i * (uint)Marshal.SizeOf(typeof(CERT_EXTENSION));
                            ulong  next              = offset + (ulong)nativeExtensions.rgExtension.ToInt64();
                            IntPtr nextExtensionAddr = new IntPtr((long)next);

                            Marshal.StructureToPtr(nativeExtensionArray[i], nextExtensionAddr, false);
                        }
                    }
                }

                // Setup a CRYPT_KEY_PROV_INFO for the key
                CRYPT_KEY_PROV_INFO keyProvInfo = new CRYPT_KEY_PROV_INFO();
                keyProvInfo.pwszContainerName = key.UniqueName;
                keyProvInfo.pwszProvName      = key.Provider.Provider;
                keyProvInfo.dwProvType        = 0; // NCRYPT
                keyProvInfo.dwFlags           = 0;
                keyProvInfo.cProvParam        = 0;
                keyProvInfo.rgProvParam       = IntPtr.Zero;
                keyProvInfo.dwKeySpec         = 0;

                //
                // Now that all of the needed data structures are setup, we can create the certificate
                //

                SafeCertContextHandle selfSignedCertHandle = null;
                unsafe
                {
                    fixed(byte *pSubjectName = &subjectName[0])
                    {
                        // Create a CRYPTOAPI_BLOB for the subject of the cert
                        CapiNative.CRYPTOAPI_BLOB nativeSubjectName = new CapiNative.CRYPTOAPI_BLOB();
                        nativeSubjectName.cbData = subjectName.Length;
                        nativeSubjectName.pbData = new IntPtr(pSubjectName);

                        // Now that we've converted all the inputs to native data structures, we can generate
                        // the self signed certificate for the input key.
                        using (SafeNCryptKeyHandle keyHandle = key.Handle)
                        {
                            selfSignedCertHandle =
                                UnsafeNativeMethods.CertCreateSelfSignCertificate(keyHandle,
                                                                                  ref nativeSubjectName,
                                                                                  creationOptions,
                                                                                  ref keyProvInfo,
                                                                                  ref nativeSignatureAlgorithm,
                                                                                  ref nativeStartTime,
                                                                                  ref nativeEndTime,
                                                                                  ref nativeExtensions);
                            if (selfSignedCertHandle.IsInvalid)
                            {
                                throw new CryptographicException(Marshal.GetLastWin32Error());
                            }
                        }
                    }
                }

                Debug.Assert(selfSignedCertHandle != null, "selfSignedCertHandle != null");

                // Attach a key context to the certificate which will allow Windows to find the private key
                // associated with the certificate if the NCRYPT_KEY_HANDLE is ephemeral.
                // is done.
                using (SafeNCryptKeyHandle keyHandle = key.Handle)
                {
                    CERT_KEY_CONTEXT keyContext = new CERT_KEY_CONTEXT();
                    keyContext.cbSize     = Marshal.SizeOf(typeof(CERT_KEY_CONTEXT));
                    keyContext.hNCryptKey = keyHandle.DangerousGetHandle();
                    keyContext.dwKeySpec  = KeySpec.NCryptKey;

                    bool attachedProperty = false;
                    int  setContextError  = 0;

                    // Run in a CER to ensure accurate tracking of the transfer of handle ownership
                    RuntimeHelpers.PrepareConstrainedRegions();
                    try { }
                    finally
                    {
                        CertificatePropertySetFlags flags = CertificatePropertySetFlags.None;
                        if (!takeOwnershipOfKey)
                        {
                            // If the certificate is not taking ownership of the key handle, then it should
                            // not release the handle when the context is released.
                            flags |= CertificatePropertySetFlags.NoCryptRelease;
                        }

                        attachedProperty =
                            UnsafeNativeMethods.CertSetCertificateContextProperty(selfSignedCertHandle,
                                                                                  CertificateProperty.KeyContext,
                                                                                  flags,
                                                                                  ref keyContext);
                        setContextError = Marshal.GetLastWin32Error();

                        // If we succesfully transferred ownership of the key to the certificate,
                        // then we need to ensure that we no longer release its handle.
                        if (attachedProperty && takeOwnershipOfKey)
                        {
                            keyHandle.SetHandleAsInvalid();
                        }
                    }

                    if (!attachedProperty)
                    {
                        throw new CryptographicException(setContextError);
                    }
                }

                return(selfSignedCertHandle);
            }
            finally
            {
                //
                // In order to release all resources held by the CERT_EXTENSIONS we need to do three things
                //   1. Destroy each structure marshaled into the native CERT_EXTENSION array
                //   2. Release the memory used for the CERT_EXTENSION array
                //   3. Release the memory used in each individual CERT_EXTENSION
                //

                // Release each extension marshaled into the native buffer as well
                if (nativeExtensions.rgExtension != IntPtr.Zero)
                {
                    for (int i = 0; i < nativeExtensionArray.Length; ++i)
                    {
                        ulong  offset            = (uint)i * (uint)Marshal.SizeOf(typeof(CERT_EXTENSION));
                        ulong  next              = offset + (ulong)nativeExtensions.rgExtension.ToInt64();
                        IntPtr nextExtensionAddr = new IntPtr((long)next);

                        Marshal.DestroyStructure(nextExtensionAddr, typeof(CERT_EXTENSION));
                    }

                    Marshal.FreeCoTaskMem(nativeExtensions.rgExtension);
                }

                // If we allocated memory for any extensions, make sure to free it now
                for (int i = 0; i < nativeExtensionArray.Length; ++i)
                {
                    if (nativeExtensionArray[i].Value.pbData != IntPtr.Zero)
                    {
                        Marshal.FreeCoTaskMem(nativeExtensionArray[i].Value.pbData);
                    }
                }
            }
        }
        // returns a list of IntPtr's; the first IntPtr is the pointer to the CERT_EXTENSIONS
        // strucuture; the other pointers are pointers that have to be released in order
        // to avoid leaking memory
        private static unsafe List<IntPtr> ConvertExtensions(X509ExtensionCollection extensions)
        {
            List<IntPtr> ret = new List<IntPtr>();
            if (extensions == null && extensions.Count == 0) {
                ret.Add(IntPtr.Zero);
                return ret;
            }

            int extensionStructSize = Marshal.SizeOf(typeof(CERT_EXTENSION));
            // create the pointer to the CERT_EXTENSIONS object
            CERT_EXTENSIONS extensionsStruct = new CERT_EXTENSIONS();
            IntPtr extensionsPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(CERT_EXTENSIONS)));
            ret.Add(extensionsPtr);
            extensionsStruct.cExtension = extensions.Count;
            extensionsStruct.rgExtension = Marshal.AllocHGlobal(extensionStructSize * extensions.Count); ;
            ret.Add(extensionsStruct.rgExtension);
            Marshal.StructureToPtr(extensionsStruct, extensionsPtr, false);

            // create the array of CERT_EXTENSION objects
            CERT_EXTENSION extensionStruct = new CERT_EXTENSION();
            byte* workPointer = (byte*)extensionsStruct.rgExtension.ToPointer();
            foreach(X509Extension ext in extensions) {
                // initialize the extension structure
                extensionStruct.pszObjId = Marshal.StringToHGlobalAnsi(ext.Oid.Value);
                ret.Add(extensionStruct.pszObjId);
                extensionStruct.fCritical = ext.Critical ? 1 : 0;
                byte[] rawData = ext.RawData;
                extensionStruct.cbData = rawData.Length;
                extensionStruct.pbData = Marshal.AllocHGlobal(rawData.Length); ;
                Marshal.Copy(rawData, 0, extensionStruct.pbData, rawData.Length);
                ret.Add(extensionStruct.pbData);
                // copy it to unmanaged memory
                Marshal.StructureToPtr(extensionStruct, new IntPtr(workPointer), false);
                workPointer += extensionStructSize;
            }
            // everything successfully created; return
            return ret;
        }
        // creates the alternate name extension for the certificate.
        static void CreateSubjectAltNameExtension(
	        string applicationUri,
	        IList<string> hostNames,
	        ref CERT_EXTENSION pExtension)
        {
	        int count = hostNames.Count + 1;
            
	        // initialize extension.
	        pExtension.pszObjId  = szOID_SUBJECT_ALT_NAME2;
	        pExtension.fCritical = 0;
        
            IntPtr pData = IntPtr.Zero;
            int dwDataSize = 0;

	        // build list of alternate names.
            IntPtr pAlternateNames = IntPtr.Zero;
	        IntPtr pEntries = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(CERT_ALT_NAME_ENTRY))*count);

	        // create structure to encode.

	        try
	        {
		        // set application uri.
                CERT_ALT_NAME_ENTRY pEntry = new CERT_ALT_NAME_ENTRY();

		        pEntry.dwAltNameChoice = CERT_ALT_NAME_URL;
		        pEntry.Value.pwszURL = Marshal.StringToHGlobalUni(applicationUri);

                Marshal.StructureToPtr(pEntry, pEntries, false);
                IntPtr pPos = new IntPtr(pEntries.ToInt64() + Marshal.SizeOf(typeof(CERT_ALT_NAME_ENTRY)));

		        for (int ii = 0; ii < hostNames.Count; ii++)
		        {
			        System.Net.IPAddress ipAddress = null;

			        // check for ip address.
			        if (System.Net.IPAddress.TryParse(hostNames[ii], out ipAddress))
			        {
				        byte[] bytes = ipAddress.GetAddressBytes();

				        pEntry.dwAltNameChoice  = CERT_ALT_NAME_IP_ADDRESS;
				        pEntry.Value.IPAddress.cbData = bytes.Length;
				        pEntry.Value.IPAddress.pbData = AllocBytes(bytes);
			        }

			        // treat as DNS host name.
			        else
			        {
				        pEntry.dwAltNameChoice = CERT_ALT_NAME_DNS_NAME;
				        pEntry.Value.pwszDNSName = Marshal.StringToHGlobalUni(hostNames[ii]);
			        }

                    Marshal.StructureToPtr(pEntry, pPos, false);
                    pPos = new IntPtr(pPos.ToInt64() + Marshal.SizeOf(typeof(CERT_ALT_NAME_ENTRY)));
		        }
        		
	            CERT_ALT_NAME_INFO alternateNames = new CERT_ALT_NAME_INFO();

	            alternateNames.cAltEntry  = count;
	            alternateNames.rgAltEntry = pEntries;

                pAlternateNames = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(CERT_ALT_NAME_INFO)));
                Marshal.StructureToPtr(alternateNames, pAlternateNames, false);

		        // calculate amount of memory required.
		        int bResult = NativeMethods.CryptEncodeObjectEx(
			        X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
			        szOID_SUBJECT_ALT_NAME2, // X509_ALTERNATE_NAME,
			        pAlternateNames,
	                0,
	                IntPtr.Zero,
	                IntPtr.Zero,
	                ref dwDataSize);
                                               
                if (bResult == 0)
		        {
			        throw new InvalidOperationException("Could not get size for subject alternate name extension.");
		        }

	            // allocate memory.
	            pData = Marshal.AllocHGlobal(dwDataSize);

		        // encode blob.
		        bResult = NativeMethods.CryptEncodeObjectEx(
			        X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
			        szOID_SUBJECT_ALT_NAME2, // X509_ALTERNATE_NAME,
			        pAlternateNames,
	                0,
	                IntPtr.Zero,
	                pData,
	                ref dwDataSize);

		        if (bResult == 0)
		        {
			        throw new InvalidOperationException("Could not create subject alternate name extension.");
		        }

                pExtension.Value.cbData = dwDataSize;
                pExtension.Value.pbData = pData;
                pData = IntPtr.Zero;
	        }
	        finally
	        {
                if (pData != IntPtr.Zero)
                {
                    Marshal.FreeHGlobal(pData);
                }
                
                if (pAlternateNames != IntPtr.Zero)
                {
                    Marshal.DestroyStructure(pAlternateNames, typeof(CERT_ALT_NAME_INFO));
                    Marshal.FreeHGlobal(pAlternateNames);
                }

		        if (pEntries != IntPtr.Zero)
		        {
                    IntPtr pPos = pEntries;

			        for (int ii = 0; ii < count; ii++)
			        {
                        CERT_ALT_NAME_ENTRY pEntry = (CERT_ALT_NAME_ENTRY)Marshal.PtrToStructure(pPos, typeof(CERT_ALT_NAME_ENTRY));
                        pPos = new IntPtr(pPos.ToInt64() + Marshal.SizeOf(typeof(CERT_ALT_NAME_ENTRY)));

				        switch (pEntry.dwAltNameChoice)
				        {
					        case CERT_ALT_NAME_URL:
					        {
						        Marshal.FreeHGlobal(pEntry.Value.pwszURL);
						        break;
					        }
        					
					        case CERT_ALT_NAME_DNS_NAME:
					        {
						        Marshal.FreeHGlobal(pEntry.Value.pwszDNSName);
						        break;
					        }

					        case CERT_ALT_NAME_IP_ADDRESS:
					        {
						        Marshal.FreeHGlobal(pEntry.Value.IPAddress.pbData);
						        break;
					        }
				        }
			        }

			        Marshal.FreeHGlobal(pEntries);
		        }
	        }
        }
        // creates the extended key usage extension
        static void CreateExtendedKeyUsageExtension(ref CERT_EXTENSION pExtension)
        {
	        // build list of allowed key uses
            IntPtr[] allowedUses = new IntPtr[2];

	        allowedUses[0] = Marshal.StringToHGlobalAnsi(szOID_PKIX_KP_SERVER_AUTH);
	        allowedUses[1] = Marshal.StringToHGlobalAnsi(szOID_PKIX_KP_CLIENT_AUTH);

            CERT_ENHKEY_USAGE usage;

	        usage.cUsageIdentifier = 2;
	        usage.rgpszUsageIdentifier = Marshal.AllocHGlobal(IntPtr.Size*2);
            Marshal.Copy(allowedUses, 0, usage.rgpszUsageIdentifier, allowedUses.Length);

	        // initialize extension.
	        pExtension.pszObjId  = szOID_ENHANCED_KEY_USAGE;
	        pExtension.fCritical = 1;
            
            GCHandle hUsage = GCHandle.Alloc(usage, GCHandleType.Pinned);
            IntPtr pData = IntPtr.Zero;
            int dwDataSize = 0;

            try
            {
	            // calculate amount of memory required.
	            int bResult = NativeMethods.CryptEncodeObjectEx(
		            X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
		            szOID_ENHANCED_KEY_USAGE, // X509_ENHANCED_KEY_USAGE,
	                hUsage.AddrOfPinnedObject(),
	                0,
	                IntPtr.Zero,
	                IntPtr.Zero,
	                ref dwDataSize);

	            if (bResult == 0)
	            {
		            throw new InvalidOperationException("Could not get size for extended key usage extension.");
	            }

	            // allocate memory.
	            pData = Marshal.AllocHGlobal(dwDataSize);

	            // encode blob.
	            bResult = NativeMethods.CryptEncodeObjectEx(
		            X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
		            szOID_ENHANCED_KEY_USAGE, // X509_ENHANCED_KEY_USAGE,
	                hUsage.AddrOfPinnedObject(),
	                0,
	                IntPtr.Zero,
	                pData,
	                ref dwDataSize);

	            if (bResult == 0)
	            {
		            throw new InvalidOperationException("Could not create extended key usage extension.");
	            }

                pExtension.Value.cbData = dwDataSize;
                pExtension.Value.pbData = pData;
                pData = IntPtr.Zero;
            }
            finally
            {
                if (pData != IntPtr.Zero)
                {
                    Marshal.FreeHGlobal(pData);
                }

                Marshal.FreeHGlobal(allowedUses[0]);
                Marshal.FreeHGlobal(allowedUses[1]);
                Marshal.FreeHGlobal(usage.rgpszUsageIdentifier);

                if (hUsage.IsAllocated)
                {
                    hUsage.Free();
                }
            }
        }
        // creates the key usage constraints extension.
        static void CreateKeyUsageExtension(ref CERT_EXTENSION pExtension, bool isCA)
        {
	        // build list of allowed key uses
	        int allowedUses = 0;

	        if (isCA)
	        {
		        allowedUses |= CERT_KEY_CERT_SIGN_KEY_USAGE;
		        allowedUses |= CERT_OFFLINE_CRL_SIGN_KEY_USAGE;
		        allowedUses |= CERT_CRL_SIGN_KEY_USAGE;
		        allowedUses |= CERT_NON_REPUDIATION_KEY_USAGE;
	        }
	        else
	        {
		        allowedUses |= CERT_DATA_ENCIPHERMENT_KEY_USAGE;
		        allowedUses |= CERT_DIGITAL_SIGNATURE_KEY_USAGE;
		        allowedUses |= CERT_KEY_ENCIPHERMENT_KEY_USAGE;
		        allowedUses |= CERT_NON_REPUDIATION_KEY_USAGE;
		        allowedUses |= CERT_KEY_CERT_SIGN_KEY_USAGE;
	        }
            
            GCHandle hAllowedUses = GCHandle.Alloc(allowedUses, GCHandleType.Pinned);

	        CRYPT_BIT_BLOB usage;

	        usage.cbData = 1;
	        usage.pbData = hAllowedUses.AddrOfPinnedObject();
	        usage.cUnusedBits = 0;

	        // initialize extension.
	        pExtension.pszObjId  = szOID_KEY_USAGE;
	        pExtension.fCritical = 1;
            
            GCHandle hUsage = GCHandle.Alloc(usage, GCHandleType.Pinned);
            IntPtr pData = IntPtr.Zero;
            int dwDataSize = 0;

            try
            {
	            // calculate amount of memory required.
	            int bResult = NativeMethods.CryptEncodeObjectEx(
		            X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
		            szOID_KEY_USAGE, // X509_KEY_USAGE,
		            hUsage.AddrOfPinnedObject(),
		            0,
		            IntPtr.Zero,
	                IntPtr.Zero,
		            ref dwDataSize);

	            if (bResult == 0)
	            {
		            throw new InvalidOperationException("Could not get size for key usage extension.");
	            }

	            // allocate memory.
	            pData = Marshal.AllocHGlobal(dwDataSize);

	            // encode blob.
	            bResult = NativeMethods.CryptEncodeObjectEx(
		            X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
		            szOID_KEY_USAGE, // X509_KEY_USAGE,
		            hUsage.AddrOfPinnedObject(),
		            0,
		            IntPtr.Zero,
		            pData,
		            ref dwDataSize);

	            if (bResult == 0)
	            {
		            throw new InvalidOperationException("Could not create key usage extension.");
	            }

                pExtension.Value.cbData = dwDataSize;
                pExtension.Value.pbData = pData;
                pData = IntPtr.Zero;
            }
            finally
            {
                if (pData != IntPtr.Zero)
                {
                    Marshal.FreeHGlobal(pData);
                }

                if (hAllowedUses.IsAllocated)
                {
                    hAllowedUses.Free();
                }

                if (hUsage.IsAllocated)
                {
                    hUsage.Free();
                }
            }
        }
        // creates the basic constraints extension.
        static void CreateBasicConstraintsExtension(ref CERT_EXTENSION pExtension, bool isCA)
        {
	        // set the certificate as a non-CA certificate.
	        CERT_BASIC_CONSTRAINTS2_INFO constraints;

	        constraints.fCA = (isCA)?1:0;
	        constraints.fPathLenConstraint = 0;
	        constraints.dwPathLenConstraint = 0;

	        pExtension.pszObjId  = szOID_BASIC_CONSTRAINTS2;
	        pExtension.fCritical = 1;
            
            GCHandle hConstraints = GCHandle.Alloc(constraints, GCHandleType.Pinned);
            IntPtr pData = IntPtr.Zero;
            int dwDataSize = 0;

            try
            {
	            // calculate amount of memory required.
	            int bResult = NativeMethods.CryptEncodeObjectEx(
		            X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
		            szOID_BASIC_CONSTRAINTS2, // X509_BASIC_CONSTRAINTS2,
		            hConstraints.AddrOfPinnedObject(),
		            0,
		            IntPtr.Zero,
	                IntPtr.Zero,
		            ref dwDataSize);

	            if (bResult == 0)
	            {
		            throw new InvalidOperationException("Could not get size for basic constraints extension.");
	            }

	            // allocate memory.
	            pData = Marshal.AllocHGlobal(dwDataSize);

	            // encode blob.
	            bResult = NativeMethods.CryptEncodeObjectEx(
		            X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
		            szOID_BASIC_CONSTRAINTS2, // X509_BASIC_CONSTRAINTS2,
		            hConstraints.AddrOfPinnedObject(),
		            0,
		            IntPtr.Zero,
		            pData,
		            ref dwDataSize);

	            if (bResult == 0)
	            {
		            throw new InvalidOperationException("Could not create for basic constraints extension.");
	            }

                pExtension.Value.cbData = dwDataSize;
                pExtension.Value.pbData = pData;
                pData = IntPtr.Zero;
            }
            finally
            {
                if (pData != IntPtr.Zero)
                {
                    Marshal.FreeHGlobal(pData);
                }

                if (hConstraints.IsAllocated)
                {
                    hConstraints.Free();
                }
            }
        }
        // creates the basic constraints extension.
        static void CreateAuthorityKeyIdentifierExtension(
            ref CERT_EXTENSION pExtension,
            ref CRYPT_DATA_BLOB pKeyId)
        {
	        // set the certificate as a non-CA certificate.
            CERT_AUTHORITY_KEY_ID2_INFO keyInfo = new CERT_AUTHORITY_KEY_ID2_INFO();

	        keyInfo.KeyId.cbData = pKeyId.cbData;
	        keyInfo.KeyId.pbData = pKeyId.pbData;

	        pExtension.pszObjId  = szOID_AUTHORITY_KEY_IDENTIFIER2;
	        pExtension.fCritical = 0;
            
            GCHandle hKeyInfo = GCHandle.Alloc(keyInfo, GCHandleType.Pinned);
            IntPtr pData = IntPtr.Zero;
            int dwDataSize = 0;

            try
            {
	            // calculate amount of memory required.
                int bResult = NativeMethods.CryptEncodeObjectEx(
		            X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
		            szOID_AUTHORITY_KEY_IDENTIFIER2, // X509_AUTHORITY_KEY_ID,
		            hKeyInfo.AddrOfPinnedObject(),
		            0,
		            IntPtr.Zero,
	                IntPtr.Zero,
		            ref dwDataSize);

	            if (bResult == 0)
	            {
		            throw new InvalidOperationException("Could not get size for basic constraints extension.");
	            }

	            // allocate memory.
	            pData = Marshal.AllocHGlobal(dwDataSize);

	            // encode blob.
                bResult = NativeMethods.CryptEncodeObjectEx(
		            X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
		            szOID_AUTHORITY_KEY_IDENTIFIER2, // X509_AUTHORITY_KEY_ID,
		            hKeyInfo.AddrOfPinnedObject(),
		            0,
		            IntPtr.Zero,
		            pData,
		            ref dwDataSize);

	            if (bResult == 0)
	            {
		            throw new InvalidOperationException("Could not create for basic constraints extension.");
	            }

                pExtension.Value.cbData = dwDataSize;
                pExtension.Value.pbData = pData;
                pData = IntPtr.Zero;
            }
            finally
            {
                if (pData != IntPtr.Zero)
                {
                    Marshal.FreeHGlobal(pData);
                }

                if (hKeyInfo.IsAllocated)
                {
                    hKeyInfo.Free();
                }
            }
        }
        // creates the basic constraints extension.
        private static void CreateSubjectKeyIdentifierExtension(ref CERT_EXTENSION pExtension, ref CRYPT_DATA_BLOB pKeyId)
        {
	        pExtension.pszObjId  = szOID_SUBJECT_KEY_IDENTIFIER;
	        pExtension.fCritical = 0;

            GCHandle hKey = GCHandle.Alloc(pKeyId, GCHandleType.Pinned);
            IntPtr pData = IntPtr.Zero;
            int dwDataSize = 0;

            try
            {
	            // calculate amount of memory required.
                int bResult = NativeMethods.CryptEncodeObjectEx(
		            X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
		            szOID_SUBJECT_KEY_IDENTIFIER,
		            hKey.AddrOfPinnedObject(),
		            0,
		            IntPtr.Zero,
	                IntPtr.Zero,
		            ref dwDataSize);

	            if (bResult == 0)
	            {
		            throw new InvalidOperationException("Could not get size for subject key info extension.");
	            }

	            // allocate memory.
	            pData = Marshal.AllocHGlobal(dwDataSize);

	            // encode blob.
                bResult = NativeMethods.CryptEncodeObjectEx(
		            X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
		            szOID_SUBJECT_KEY_IDENTIFIER,
		            hKey.AddrOfPinnedObject(),
		            0,
		            IntPtr.Zero,
		            pData,
		            ref dwDataSize);

	            if (bResult == 0)
	            {
                    throw new InvalidOperationException("Could not create for subject key info extension.");
	            }

                pExtension.Value.cbData = dwDataSize;
                pExtension.Value.pbData = pData;
                pData = IntPtr.Zero;
            }
            finally
            {
                if (pData != IntPtr.Zero)
                {
                    Marshal.FreeHGlobal(pData);
                }

                if (hKey.IsAllocated)
                {
                    hKey.Free();
                }
            }
        }
        /// <summary>
        /// Creates the certificate and adds it to the store.
        /// </summary>
        private static string CreateSelfSignedCertificate(
            IntPtr hProvider,
            IntPtr hStore,
            bool useMachineStore,
            string applicationName,
            string applicationUri,
            string subjectName,
            IList<string> hostNames,
            ushort keySize,
            ushort lifetimeInMonths,
            ushort algorithm = 0)
        {
            IntPtr hKey = IntPtr.Zero;
            IntPtr pKpi = IntPtr.Zero;
            IntPtr pThumbprint = IntPtr.Zero;
            IntPtr pContext = IntPtr.Zero;
            IntPtr pAlgorithmId = IntPtr.Zero;
            IntPtr pNewContext = IntPtr.Zero;
            CRYPT_DATA_BLOB publicKeyId = new CRYPT_DATA_BLOB();
            CERT_NAME_BLOB subjectNameBlob = new CERT_NAME_BLOB();
            SYSTEMTIME stValidTo = new SYSTEMTIME();
            CERT_EXTENSIONS extensions = new CERT_EXTENSIONS();
            CRYPT_DATA_BLOB friendlyName = new CRYPT_DATA_BLOB();

            GCHandle hValidTo = new GCHandle();
            GCHandle hExtensionList = new GCHandle();
            GCHandle hSubjectNameBlob = new GCHandle();
            GCHandle hFriendlyName = new GCHandle();

            try
            {
                // create a new key pair.
                int bResult = NativeMethods.CryptGenKey(
                    hProvider,
                    AT_KEYEXCHANGE,
                    CRYPT_EXPORTABLE | (keySize << 16),
                    ref hKey);

                if (bResult == 0)
                {
                    Throw("Could not generate a new key pair. Error={0:X8}", Marshal.GetLastWin32Error());
                }

                // gey the public key identifier.
                GetPublicKeyIdentifier(hProvider, ref publicKeyId);

                // construct the certificate subject name.
                CreateX500Name(subjectName, ref subjectNameBlob);
                GCHandle hSubjectName = GCHandle.Alloc(subjectNameBlob, GCHandleType.Pinned);

                // allocate memory for all possible extensions.
                extensions.cExtension = 0;
                extensions.rgExtension = Marshal.AllocHGlobal(6 * Marshal.SizeOf(typeof(CERT_EXTENSION)));

                // create the subject key info extension.
                IntPtr pPos = extensions.rgExtension;
                CERT_EXTENSION extension = new CERT_EXTENSION();
                CreateSubjectKeyIdentifierExtension(ref extension, ref publicKeyId);
                Marshal.StructureToPtr(extension, pPos, false);
                pPos = new IntPtr(pPos.ToInt64() + Marshal.SizeOf(typeof(CERT_EXTENSION)));
                extensions.cExtension++;

                // create the authority key info extension.
                extension = new CERT_EXTENSION();
                CreateAuthorityKeyIdentifierExtension(ref extension, ref publicKeyId);
                Marshal.StructureToPtr(extension, pPos, false);
                pPos = new IntPtr(pPos.ToInt64() + Marshal.SizeOf(typeof(CERT_EXTENSION)));
                extensions.cExtension++;

                // create the basic constraints extension.
                extension = new CERT_EXTENSION();
                CreateBasicConstraintsExtension(ref extension, false);
                Marshal.StructureToPtr(extension, pPos, false);
                pPos = new IntPtr(pPos.ToInt64() + Marshal.SizeOf(typeof(CERT_EXTENSION)));
                extensions.cExtension++;

                // create the key usage extension.
                extension = new CERT_EXTENSION();
                CreateKeyUsageExtension(ref extension, false);
                Marshal.StructureToPtr(extension, pPos, false);
                pPos = new IntPtr(pPos.ToInt64() + Marshal.SizeOf(typeof(CERT_EXTENSION)));
                extensions.cExtension++;

                // create the extended key usage extension.
                extension = new CERT_EXTENSION();
                CreateExtendedKeyUsageExtension(ref extension);
                Marshal.StructureToPtr(extension, pPos, false);
                pPos = new IntPtr(pPos.ToInt64() + Marshal.SizeOf(typeof(CERT_EXTENSION)));
                extensions.cExtension++;

                // create the subject alternate name extension.
                extension = new CERT_EXTENSION();
                CreateSubjectAltNameExtension(applicationUri, hostNames, ref extension);
                Marshal.StructureToPtr(extension, pPos, false);
                pPos = new IntPtr(pPos.ToInt64() + Marshal.SizeOf(typeof(CERT_EXTENSION)));
                extensions.cExtension++;

                // set the expiration date.
                DateTime validTo = DateTime.UtcNow.AddMonths(lifetimeInMonths);
                System.Runtime.InteropServices.ComTypes.FILETIME ftValidTo = new System.Runtime.InteropServices.ComTypes.FILETIME();
                ulong ticks = (ulong)(validTo.Ticks - new DateTime(1601, 1, 1).Ticks);
                ftValidTo.dwHighDateTime = (int)((0xFFFFFFFF00000000 & (ulong)ticks) >> 32);
                ftValidTo.dwLowDateTime = (int)((ulong)ticks & 0x00000000FFFFFFFF);

                NativeMethods.FileTimeToSystemTime(ref ftValidTo, ref stValidTo);

                // specify what key is being used to sign the certificate.
                CRYPT_KEY_PROV_INFO kpi = new CRYPT_KEY_PROV_INFO();

                kpi.pwszContainerName = KEY_CONTAINER_NAME; // must be the same as the hProvider
                kpi.pwszProvName = DEFAULT_CRYPTO_PROVIDER;
                kpi.dwProvType = PROV_RSA_FULL;
                kpi.dwFlags = CERT_SET_KEY_CONTEXT_PROP_ID;
                kpi.dwKeySpec = AT_KEYEXCHANGE;

                if (useMachineStore)
                {
                    kpi.dwFlags |= CRYPT_MACHINE_KEYSET;
                }
                else
                {
                    kpi.dwFlags |= CRYPT_USER_KEYSET;
                }

                pKpi = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(CRYPT_KEY_PROV_INFO)));
                Marshal.StructureToPtr(kpi, pKpi, false);

                hValidTo = GCHandle.Alloc(stValidTo, GCHandleType.Pinned);
                hExtensionList = GCHandle.Alloc(extensions, GCHandleType.Pinned);
                hSubjectNameBlob = GCHandle.Alloc(subjectNameBlob, GCHandleType.Pinned);

                if (algorithm == 1)
                {
                    CRYPT_ALGORITHM_IDENTIFIER algorithmID = new CRYPT_ALGORITHM_IDENTIFIER();
                    algorithmID.pszObjId = "1.2.840.113549.1.1.11"; //SHA256

                    pAlgorithmId = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(CRYPT_ALGORITHM_IDENTIFIER)));
                    Marshal.StructureToPtr(algorithmID, pAlgorithmId, false);

                    //create the certificate
                    pContext = NativeMethods.CertCreateSelfSignCertificate(
                         hProvider,
                         hSubjectNameBlob.AddrOfPinnedObject(),
                         0,
                         pKpi,
                         pAlgorithmId,
                         IntPtr.Zero,
                         hValidTo.AddrOfPinnedObject(),
                         hExtensionList.AddrOfPinnedObject());
                }
                else
                {
                    // (default) create the certificate.
                    pContext = NativeMethods.CertCreateSelfSignCertificate(
                    hProvider,
                    hSubjectNameBlob.AddrOfPinnedObject(),
                    0,
                    pKpi,
                    IntPtr.Zero,
                    IntPtr.Zero,
                    hValidTo.AddrOfPinnedObject(),
                    hExtensionList.AddrOfPinnedObject());
                }

                if (pContext == IntPtr.Zero)
                {
                    Throw("Could not create self-signed certificate. Error={0:X8}", Marshal.GetLastWin32Error());
                }

                // get the thumbprint.
                int dwThumbprintSize = 20;
                pThumbprint = Marshal.AllocHGlobal(dwThumbprintSize);

                bResult = NativeMethods.CertGetCertificateContextProperty(
                    pContext,
                    CERT_SHA1_HASH_PROP_ID,
                    pThumbprint,
                    ref dwThumbprintSize);

                if (bResult == 0)
                {
                    Throw("Could not get the thumbprint of the new certificate. Error={0:X8}", Marshal.GetLastWin32Error());
                }

                byte[] bytes = new byte[dwThumbprintSize];
                Marshal.Copy(pThumbprint, bytes, 0, dwThumbprintSize);
                string thumbprint = Utils.ToHexString(bytes);

                // set the friendly name.
                friendlyName.pbData = Marshal.StringToHGlobalUni(applicationName);
                friendlyName.cbData = (applicationName.Length+1)*Marshal.SizeOf(typeof(ushort));
                hFriendlyName = GCHandle.Alloc(friendlyName, GCHandleType.Pinned);

                bResult = NativeMethods.CertSetCertificateContextProperty(
                    pContext,
                    CERT_FRIENDLY_NAME_PROP_ID,
                    0,
                    hFriendlyName.AddrOfPinnedObject());

                if (bResult == 0)
                {
                    Throw("Could not set the friendly name for the certificate. Error={0:X8}", Marshal.GetLastWin32Error());
                }

                // add into store.
                bResult = NativeMethods.CertAddCertificateContextToStore(
                    hStore,
                    pContext,
                    CERT_STORE_ADD_REPLACE_EXISTING,
                    ref pNewContext);

                if (bResult == 0)
                {
                    Throw("Could not add the certificate to the store. Error={0:X8}", Marshal.GetLastWin32Error());
                }

                return thumbprint;
            }
            finally
            {
                if (pContext != IntPtr.Zero)
                {
                    NativeMethods.CertFreeCertificateContext(pContext);
                }

                if (pNewContext != IntPtr.Zero)
                {
                    NativeMethods.CertFreeCertificateContext(pNewContext);
                }

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

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

                if (pAlgorithmId != IntPtr.Zero)
                {
                    Marshal.DestroyStructure(pAlgorithmId, typeof(CRYPT_ALGORITHM_IDENTIFIER));
                    Marshal.FreeHGlobal(pAlgorithmId);
                }

                if (hValidTo.IsAllocated) hValidTo.Free();
                if (hExtensionList.IsAllocated) hExtensionList.Free();
                if (hSubjectNameBlob.IsAllocated) hSubjectNameBlob.Free();
                if (hFriendlyName.IsAllocated) hFriendlyName.Free();

                if (pKpi != IntPtr.Zero)
                {
                    Marshal.DestroyStructure(pKpi, typeof(CRYPT_KEY_PROV_INFO));
                    Marshal.FreeHGlobal(pKpi);
                }

                DeleteExtensions(ref extensions.rgExtension, extensions.cExtension);
                DeleteX500Name(ref subjectNameBlob);

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

                if (hKey != IntPtr.Zero)
                {
                    NativeMethods.CryptDestroyKey(hKey);
                }
            }
        }