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); } } } }
private static extern bool CertSetCertificateContextProperty(SafeCertContextHandle pCertContext, CertificateProperty dwPropId, CertificatePropertySetFlags dwFlags, [In] ref CERT_KEY_CONTEXT pvData);
internal static extern bool CertSetCertificateContextProperty(SafeCertContextHandle pCertContext, CertificateProperty dwPropId, CertificatePropertySetFlags dwFlags, [In] ref CERT_KEY_CONTEXT pvData);
private static SafeCertContextHandle CreateSelfSignedCertificate(CngKey key, bool takeOwnershipOfKey, byte[] subjectName, X509CertificateCreationOptions creationOptions, string signatureAlgorithmOid, DateTime startTime, DateTime endTime) { // Create an algorithm identifier structure for the signature algorithm CRYPT_ALGORITHM_IDENTIFIER nativeSignatureAlgorithm = new CRYPT_ALGORITHM_IDENTIFIER(); nativeSignatureAlgorithm.pszObjId = signatureAlgorithmOid; nativeSignatureAlgorithm.Parameters = new CRYPTOAPI_BLOB(); nativeSignatureAlgorithm.Parameters.cbData = 0; nativeSignatureAlgorithm.Parameters.pbData = IntPtr.Zero; // Convert the begin and expire dates to system time structures SYSTEMTIME nativeStartTime = new SYSTEMTIME(startTime); SYSTEMTIME nativeEndTime = new SYSTEMTIME(endTime); CERT_EXTENSIONS nativeExtensions = new CERT_EXTENSIONS(); nativeExtensions.cExtension = 0; // 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 CRYPTOAPI_BLOB nativeSubjectName = new 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 = 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 = 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); }