private T GetPrivateKey <T>(X509Certificate2 certificate, bool silent, bool preferNCrypt) where T : AsymmetricAlgorithm { if (!certificate.HasPrivateKey) { return(null); } SafeProvOrNCryptKeyHandle handle = GetCertificatePrivateKey( certificate, silent, preferNCrypt, out CryptKeySpec keySpec, out Exception exception); using (handle) { if (handle == null || handle.IsInvalid) { if (exception != null) { throw exception; } return(null); } if (keySpec == CryptKeySpec.CERT_NCRYPT_KEY_SPEC) { using (SafeNCryptKeyHandle keyHandle = new SafeNCryptKeyHandle(handle.DangerousGetHandle(), handle)) using (CngKey cngKey = CngKey.Open(keyHandle, CngKeyHandleOpenOptions.None)) { if (typeof(T) == typeof(RSA)) { return((T)(object)new RSACng(cngKey)); } if (typeof(T) == typeof(ECDsa)) { return((T)(object)new ECDsaCng(cngKey)); } if (typeof(T) == typeof(DSA)) { return((T)(object)new DSACng(cngKey)); } Debug.Fail($"Unknown CNG key type request: {typeof(T).FullName}"); return(null); } } // The key handle is for CAPI. // Our CAPI types don't allow usage from a handle, so we have a few choices: // 1) Extract the information we need to re-open the key handle. // 2) Re-implement {R|D}SACryptoServiceProvider // 3) PNSE. // 4) Defer to cert.Get{R|D}SAPrivateKey if not silent, throw otherwise. CspParameters cspParams = handle.GetProvParameters(); Debug.Assert((cspParams.Flags & CspProviderFlags.UseExistingKey) != 0); cspParams.KeyNumber = (int)keySpec; if (silent) { cspParams.Flags |= CspProviderFlags.NoPrompt; } if (typeof(T) == typeof(RSA)) { return((T)(object)new RSACryptoServiceProvider(cspParams)); } if (typeof(T) == typeof(DSA)) { return((T)(object)new DSACryptoServiceProvider(cspParams)); } Debug.Fail($"Unknown CAPI key type request: {typeof(T).FullName}"); return(null); } }
internal static extern bool CryptAcquireCertificatePrivateKey(SafeCertContextHandle pCert, AcquireCertificateKeyOptions dwFlags, IntPtr pvReserved, // void * [Out] out SafeNCryptKeyHandle phCryptProvOrNCryptKey, [Out] out int dwKeySpec, [Out, MarshalAs(UnmanagedType.Bool)] out bool pfCallerFreeProvOrNCryptKey);
internal static unsafe ErrorCode NCryptSignHash(SafeNCryptKeyHandle hKey, void *pPaddingInfo, ReadOnlySpan <byte> pbHashValue, int cbHashValue, Span <byte> pbSignature, int cbSignature, out int pcbResult, AsymmetricPaddingMode dwFlags) => NCryptSignHash(hKey, pPaddingInfo, ref MemoryMarshal.GetReference(pbHashValue), cbHashValue, ref MemoryMarshal.GetReference(pbSignature), cbSignature, out pcbResult, dwFlags);
internal static extern ErrorCode NCryptExportKey(SafeNCryptKeyHandle hKey, IntPtr hExportKey, string pszBlobType, ref NCryptBufferDesc pParameterList, ref byte pbOutput, int cbOutput, [Out] out int pcbResult, int dwFlags);
internal static extern ErrorCode NCryptFinalizeKey(SafeNCryptKeyHandle hKey, int dwFlags);
public static unsafe bool VerifyHash(this SafeNCryptKeyHandle keyHandle, ReadOnlySpan <byte> hash, ReadOnlySpan <byte> signature, AsymmetricPaddingMode paddingMode, void *pPaddingInfo) { ErrorCode errorCode = Interop.NCrypt.NCryptVerifySignature(keyHandle, pPaddingInfo, hash, hash.Length, signature, signature.Length, paddingMode); return(errorCode == ErrorCode.ERROR_SUCCESS); // For consistency with other AsymmetricAlgorithm-derived classes, return "false" for any error code rather than making the caller catch an exception. }
internal static extern ErrorCode NCryptOpenKey(SafeNCryptProviderHandle hProvider, out SafeNCryptKeyHandle phKey, string pszKeyName, int dwLegacyKeySpec, CngKeyOpenOptions dwFlags);
internal static extern NCryptNative.ErrorCode NCryptVerifySignature(SafeNCryptKeyHandle hKey, IntPtr pPaddingInfo, [MarshalAs(UnmanagedType.LPArray)] byte[] pbHashValue, int cbHashValue, [MarshalAs(UnmanagedType.LPArray)] byte[] pbSignature, int cbSignature, int dwFlags);
private T GetPrivateKey <T>(X509Certificate2 certificate, bool silent, bool preferNCrypt) where T : AsymmetricAlgorithm { if (!certificate.HasPrivateKey) { return(null); } SafeProvOrNCryptKeyHandle handle = GetCertificatePrivateKey( certificate, silent, preferNCrypt, out CryptKeySpec keySpec, out Exception exception); using (handle) { if (handle == null || handle.IsInvalid) { if (exception != null) { throw exception; } return(null); } if (keySpec == CryptKeySpec.CERT_NCRYPT_KEY_SPEC) { using (SafeNCryptKeyHandle keyHandle = new SafeNCryptKeyHandle(handle.DangerousGetHandle(), handle)) { CngKeyHandleOpenOptions options = CngKeyHandleOpenOptions.None; byte clrIsEphemeral = 0; Interop.NCrypt.ErrorCode errorCode = Interop.NCrypt.NCryptGetByteProperty(keyHandle, "CLR IsEphemeral", ref clrIsEphemeral, CngPropertyOptions.CustomProperty); if (errorCode == Interop.NCrypt.ErrorCode.ERROR_SUCCESS && clrIsEphemeral == 1) { options |= CngKeyHandleOpenOptions.EphemeralKey; } using (CngKey cngKey = CngKey.Open(keyHandle, options)) { if (typeof(T) == typeof(RSA)) { return((T)(object)new RSACng(cngKey)); } if (typeof(T) == typeof(ECDsa)) { return((T)(object)new ECDsaCng(cngKey)); } if (typeof(T) == typeof(DSA)) { return((T)(object)new DSACng(cngKey)); } Debug.Fail($"Unknown CNG key type request: {typeof(T).FullName}"); return(null); } } } // The key handle is for CAPI. // Our CAPI types don't allow usage from a handle, so we have a few choices: // 1) Extract the information we need to re-open the key handle. // 2) Re-implement {R|D}SACryptoServiceProvider // 3) PNSE. // 4) Defer to cert.Get{R|D}SAPrivateKey if not silent, throw otherwise. CspParameters cspParams; try { cspParams = handle.GetProvParameters(); } catch (CryptographicException) { // begin: gost // - если пытаемся загрузить сертификат в non-persist попадём сюда // зовём функции x509, которые умеют работать с такими ключами if (typeof(T) == typeof(Gost3410)) { return((T)(object)certificate.GetGost3410PrivateKey()); } if (typeof(T) == typeof(Gost3410_2012_256)) { return((T)(object)certificate.GetGost3410_2012_256PrivateKey()); } if (typeof(T) == typeof(Gost3410_2012_512)) { return((T)(object)certificate.GetGost3410_2012_512PrivateKey()); } throw; // end: gost } Debug.Assert((cspParams.Flags & CspProviderFlags.UseExistingKey) != 0); cspParams.KeyNumber = (int)keySpec; if (silent) { cspParams.Flags |= CspProviderFlags.NoPrompt; } if (typeof(T) == typeof(RSA)) { return((T)(object)new RSACryptoServiceProvider(cspParams)); } if (typeof(T) == typeof(DSA)) { return((T)(object)new DSACryptoServiceProvider(cspParams)); } //begin: gost if (typeof(T) == typeof(Gost3410)) { return((T)(object)new Gost3410CryptoServiceProvider(cspParams)); } if (typeof(T) == typeof(Gost3410_2012_256)) { return((T)(object)new Gost3410_2012_256CryptoServiceProvider(cspParams)); } if (typeof(T) == typeof(Gost3410_2012_512)) { return((T)(object)new Gost3410_2012_512CryptoServiceProvider(cspParams)); } //end: gost Debug.Fail($"Unknown CAPI key type request: {typeof(T).FullName}"); return(null); } }
internal static extern NCryptNative.ErrorCode NCryptImportKey(SafeNCryptProviderHandle hProvider, IntPtr hImportKey, string pszBlobType, IntPtr pParameterList, out SafeNCryptKeyHandle phKey, [MarshalAs(UnmanagedType.LPArray)] byte[] pbData, int cbData, int dwFlags);
internal static extern NCryptNative.ErrorCode NCryptSecretAgreement(SafeNCryptKeyHandle hPrivKey, SafeNCryptKeyHandle hPubKey, out SafeNCryptSecretHandle phSecret, int dwFlags);
internal static extern NCryptNative.ErrorCode NCryptExportKey(SafeNCryptKeyHandle hKey, IntPtr hExportKey, string pszBlobType, IntPtr pParameterList, [Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbOutput, int cbOutput, out int pcbResult, int dwFlags);
internal static extern NCryptNative.ErrorCode NCryptDeleteKey(SafeNCryptKeyHandle hKey, int flags);
internal static SafeNCryptSecretHandle DeriveSecretAgreement(SafeNCryptKeyHandle privateKey, SafeNCryptKeyHandle otherPartyPublicKey) { SafeNCryptSecretHandle handle; ErrorCode code = UnsafeNativeMethods.NCryptSecretAgreement(privateKey, otherPartyPublicKey, out handle, 0); if (code != ErrorCode.Success) { throw new CryptographicException((int)code); } return(handle); }
public static extern uint NCryptSecretAgreement(SafeNCryptKeyHandle hPrivKey, SafeNCryptKeyHandle hPublicKey, out SafeNCryptSecretHandle phSecret, uint flags);
public byte[] DeriveKeyMaterial(CngKey otherPartyPublicKey) { Contract.Ensures(Contract.Result <byte[]>() != null); Contract.Assert(m_kdf >= ECDiffieHellmanKeyDerivationFunction.Hash && m_kdf <= ECDiffieHellmanKeyDerivationFunction.Tls); if (otherPartyPublicKey == null) { throw new ArgumentNullException("otherPartyPublicKey"); } if (otherPartyPublicKey.AlgorithmGroup != CngAlgorithmGroup.ECDiffieHellman) { throw new ArgumentException(SR.GetString(SR.Cryptography_ArgECDHRequiresECDHKey), "otherPartyPublicKey"); } if (otherPartyPublicKey.KeySize != KeySize) { throw new ArgumentException(SR.GetString(SR.Cryptography_ArgECDHKeySizeMismatch), "otherPartyPublicKey"); } NCryptNative.SecretAgreementFlags flags = UseSecretAgreementAsHmacKey ? NCryptNative.SecretAgreementFlags.UseSecretAsHmacKey : NCryptNative.SecretAgreementFlags.None; // We require access to the handles for generating key material. This is safe since we will never // expose these handles to user code new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert(); // This looks horribly wrong - but accessing the handle property actually returns a duplicate handle, which // we need to dispose of - otherwise, we're stuck keepign the resource alive until the GC runs. This explicitly // is not disposing of the handle underlying the key dispite what the syntax looks like. using (SafeNCryptKeyHandle localKey = Key.Handle) using (SafeNCryptKeyHandle otherKey = otherPartyPublicKey.Handle) { CodeAccessPermission.RevertAssert(); // // Generating key material is a two phase process. // 1. Generate the secret agreement // 2. Pass the secret agreement through a KDF to get key material // using (SafeNCryptSecretHandle secretAgreement = NCryptNative.DeriveSecretAgreement(localKey, otherKey)) { if (KeyDerivationFunction == ECDiffieHellmanKeyDerivationFunction.Hash) { byte[] secretAppend = SecretAppend == null ? null : SecretAppend.Clone() as byte[]; byte[] secretPrepend = SecretPrepend == null ? null : SecretPrepend.Clone() as byte[]; return(NCryptNative.DeriveKeyMaterialHash(secretAgreement, HashAlgorithm.Algorithm, secretPrepend, secretAppend, flags)); } else if (KeyDerivationFunction == ECDiffieHellmanKeyDerivationFunction.Hmac) { byte[] hmacKey = HmacKey == null ? null : HmacKey.Clone() as byte[]; byte[] secretAppend = SecretAppend == null ? null : SecretAppend.Clone() as byte[]; byte[] secretPrepend = SecretPrepend == null ? null : SecretPrepend.Clone() as byte[]; return(NCryptNative.DeriveKeyMaterialHmac(secretAgreement, HashAlgorithm.Algorithm, hmacKey, secretPrepend, secretAppend, flags)); } else { Debug.Assert(KeyDerivationFunction == ECDiffieHellmanKeyDerivationFunction.Tls, "Unknown KDF"); byte[] label = Label == null ? null : Label.Clone() as byte[]; byte[] seed = Seed == null ? null : Seed.Clone() as byte[]; if (label == null || seed == null) { throw new InvalidOperationException(SR.GetString(SR.Cryptography_TlsRequiresLabelAndSeed)); } return(NCryptNative.DeriveKeyMaterialTls(secretAgreement, label, seed, flags)); } } } }
internal static unsafe bool ExportPkcs8KeyBlob( bool allocate, SafeNCryptKeyHandle keyHandle, ReadOnlySpan <char> password, int kdfCount, Span <byte> destination, out int bytesWritten, out byte[] allocated) { using (SafeUnicodeStringHandle stringHandle = new SafeUnicodeStringHandle(password)) { fixed(byte *oidPtr = s_pkcs12TripleDesOidBytes) { Interop.NCrypt.NCryptBuffer *buffers = stackalloc Interop.NCrypt.NCryptBuffer[3]; Interop.NCrypt.PBE_PARAMS pbeParams = default; Span <byte> salt = new Span <byte>(pbeParams.rgbSalt, Interop.NCrypt.PBE_PARAMS.RgbSaltSize); RandomNumberGenerator.Fill(salt); pbeParams.Params.cbSalt = salt.Length; pbeParams.Params.iIterations = kdfCount; buffers[0] = new Interop.NCrypt.NCryptBuffer { BufferType = Interop.NCrypt.BufferType.PkcsSecret, cbBuffer = checked (2 * (password.Length + 1)), pvBuffer = stringHandle.DangerousGetHandle(), }; if (buffers[0].pvBuffer == IntPtr.Zero) { buffers[0].cbBuffer = 0; } buffers[1] = new Interop.NCrypt.NCryptBuffer { BufferType = Interop.NCrypt.BufferType.PkcsAlgOid, cbBuffer = s_pkcs12TripleDesOidBytes.Length, pvBuffer = (IntPtr)oidPtr, }; buffers[2] = new Interop.NCrypt.NCryptBuffer { BufferType = Interop.NCrypt.BufferType.PkcsAlgParam, cbBuffer = sizeof(Interop.NCrypt.PBE_PARAMS), pvBuffer = (IntPtr)(&pbeParams), }; Interop.NCrypt.NCryptBufferDesc desc = new Interop.NCrypt.NCryptBufferDesc { cBuffers = 3, pBuffers = (IntPtr)buffers, ulVersion = 0, }; Span <byte> empty = default; ErrorCode errorCode = Interop.NCrypt.NCryptExportKey( keyHandle, IntPtr.Zero, Interop.NCrypt.NCRYPT_PKCS8_PRIVATE_KEY_BLOB, ref desc, ref MemoryMarshal.GetReference(empty), 0, out int numBytesNeeded, 0); if (errorCode != ErrorCode.ERROR_SUCCESS) { throw errorCode.ToCryptographicException(); } allocated = null; if (allocate) { allocated = new byte[numBytesNeeded]; destination = allocated; } else if (numBytesNeeded > destination.Length) { bytesWritten = 0; return(false); } errorCode = Interop.NCrypt.NCryptExportKey( keyHandle, IntPtr.Zero, Interop.NCrypt.NCRYPT_PKCS8_PRIVATE_KEY_BLOB, ref desc, ref MemoryMarshal.GetReference(destination), destination.Length, out numBytesNeeded, 0); if (errorCode != ErrorCode.ERROR_SUCCESS) { throw errorCode.ToCryptographicException(); } if (allocate && numBytesNeeded != destination.Length) { byte[] trimmed = new byte[numBytesNeeded]; destination.Slice(0, numBytesNeeded).CopyTo(trimmed); Array.Clear(allocated, 0, numBytesNeeded); allocated = trimmed; } bytesWritten = numBytesNeeded; return(true); } } }
private static SafeNCryptKeyHandle?TryAcquireCngPrivateKey( SafeCertContextHandle certificateContext, out CngKeyHandleOpenOptions handleOptions) { Debug.Assert(certificateContext != null, "certificateContext != null"); Debug.Assert(!certificateContext.IsClosed && !certificateContext.IsInvalid, "!certificateContext.IsClosed && !certificateContext.IsInvalid"); IntPtr privateKeyPtr; // If the certificate has a key handle without a key prov info, return the // ephemeral key if (!certificateContext.HasPersistedPrivateKey) { int cbData = IntPtr.Size; if (Interop.crypt32.CertGetCertificateContextProperty( certificateContext, CertContextPropId.CERT_NCRYPT_KEY_HANDLE_PROP_ID, out privateKeyPtr, ref cbData)) { handleOptions = CngKeyHandleOpenOptions.EphemeralKey; return(new SafeNCryptKeyHandle(privateKeyPtr, certificateContext)); } } bool freeKey = true; SafeNCryptKeyHandle?privateKey = null; handleOptions = CngKeyHandleOpenOptions.None; try { int keySpec = 0; if (!Interop.crypt32.CryptAcquireCertificatePrivateKey( certificateContext, CryptAcquireFlags.CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG, IntPtr.Zero, out privateKey, out keySpec, out freeKey)) { int dwErrorCode = Marshal.GetLastWin32Error(); // The documentation for CryptAcquireCertificatePrivateKey says that freeKey // should already be false if "key acquisition fails", and it can be presumed // that privateKey was set to 0. But, just in case: freeKey = false; privateKey?.SetHandleAsInvalid(); return(null); } // It is very unlikely that Windows will tell us !freeKey other than when reporting failure, // because we set neither CRYPT_ACQUIRE_CACHE_FLAG nor CRYPT_ACQUIRE_USE_PROV_INFO_FLAG, which are // currently the only two success situations documented. However, any !freeKey response means the // key's lifetime is tied to that of the certificate, so re-register the handle as a child handle // of the certificate. if (!freeKey && privateKey != null && !privateKey.IsInvalid) { var newKeyHandle = new SafeNCryptKeyHandle(privateKey.DangerousGetHandle(), certificateContext); privateKey.SetHandleAsInvalid(); privateKey = newKeyHandle; freeKey = true; } return(privateKey); } catch { // If we aren't supposed to free the key, and we're not returning it, // just tell the SafeHandle to not free itself. if (privateKey != null && !freeKey) { privateKey.SetHandleAsInvalid(); } throw; } }
internal static unsafe partial bool CertSetCertificateContextProperty(SafeCertContextHandle pCertContext, CertContextPropId dwPropId, CertSetPropertyFlags dwFlags, SafeNCryptKeyHandle keyHandle);
public static CngKey Open(SafeNCryptKeyHandle keyHandle !!, CngKeyHandleOpenOptions keyHandleOpenOptions)
internal static extern ErrorCode NCryptImportKey(SafeNCryptProviderHandle hProvider, IntPtr hImportKey, string pszBlobType, ref NCryptBufferDesc pParameterList, [Out] out SafeNCryptKeyHandle phKey, ref byte pbData, int cbData, int dwFlags);
/// <summary> /// Wrap an existing key handle with a CngKey object /// </summary> public static CngKey Open(SafeNCryptKeyHandle keyHandle, CngKeyHandleOpenOptions keyHandleOpenOptions) { if (keyHandle == null) { throw new ArgumentNullException("keyHandle"); } if (keyHandle.IsClosed || keyHandle.IsInvalid) { throw new ArgumentException(SR.Cryptography_OpenInvalidHandle, "keyHandle"); } SafeNCryptKeyHandle keyHandleCopy = keyHandle.Duplicate(); // Get a handle to the key's provider. SafeNCryptProviderHandle providerHandle = new SafeNCryptProviderHandle(); IntPtr rawProviderHandle = keyHandle.GetPropertyAsIntPtr(KeyPropertyName.ProviderHandle, CngPropertyOptions.None); providerHandle.SetHandleValue(rawProviderHandle); // Set up a key object wrapping the handle CngKey key = null; try { key = new CngKey(providerHandle, keyHandleCopy); bool openingEphemeralKey = (keyHandleOpenOptions & CngKeyHandleOpenOptions.EphemeralKey) == CngKeyHandleOpenOptions.EphemeralKey; // // If we're wrapping a handle to an ephemeral key, we need to make sure that IsEphemeral is // set up to return true. In the case that the handle is for an ephemeral key that was created // by the CLR, then we don't have anything to do as the IsEphemeral CLR property will already // be setup. However, if the key was created outside of the CLR we will need to setup our // ephemeral detection property. // // This enables consumers of CngKey objects to always be able to rely on the result of // calling IsEphemeral, and also allows them to safely access the Name property. // // Finally, if we detect that this is an ephemeral key that the CLR created but we were not // told that it was an ephemeral key we'll throw an exception. This prevents us from having // to decide who to believe -- the key property or the caller of the API. Since other code // relies on the ephemeral flag being set properly to avoid tripping over bugs in CNG, we // need to reject the case that we suspect that the flag is incorrect. // if (!key.IsEphemeral && openingEphemeralKey) { key.IsEphemeral = true; } else if (key.IsEphemeral && !openingEphemeralKey) { throw new ArgumentException(SR.Cryptography_OpenEphemeralKeyHandleWithoutEphemeralFlag, "keyHandleOpenOptions"); } } catch { // Make sure that we don't leak the handle the CngKey duplicated if (key != null) { key.Dispose(); } throw; } return(key); }
internal static extern ErrorCode NCryptCreatePersistedKey(SafeNCryptProviderHandle hProvider, out SafeNCryptKeyHandle phKey, string pszAlgId, string?pszKeyName, int dwLegacyKeySpec, CngKeyCreationOptions dwFlags);
internal static partial ErrorCode NCryptImportKey(SafeNCryptProviderHandle hProvider, IntPtr hImportKey, string pszBlobType, IntPtr pParameterList, out SafeNCryptKeyHandle phKey, ref byte pbData, int cbData, int dwFlags);
internal static extern SafeCertContextHandle CertCreateSelfSignCertificate(SafeNCryptKeyHandle hCryptProvOrNCryptKey, [In] ref CapiNative.CRYPTOAPI_BLOB pSubjectIssuerBlob, X509CertificateCreationOptions dwFlags, [In] ref CRYPT_KEY_PROV_INFO pKeyProvInfo, [In] ref CapiNative.CRYPT_ALGORITHM_IDENTIFIER pSignatureAlgorithm, [In] ref Win32Native.SYSTEMTIME pStartTime, [In] ref Win32Native.SYSTEMTIME pEndTime, [In] ref CERT_EXTENSIONS pExtensions);
internal static partial ErrorCode NCryptExportKey(SafeNCryptKeyHandle hKey, IntPtr hExportKey, string pszBlobType, IntPtr pParameterList, ref byte pbOutput, int cbOutput, out int pcbResult, int dwFlags);
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); } } } }
internal static partial ErrorCode NCryptDeleteKey(SafeNCryptKeyHandle hKey, int dwFlags);
private static unsafe partial ErrorCode NCryptSignHash(SafeNCryptKeyHandle hKey, void *pPaddingInfo, ref byte pbHashValue, int cbHashValue, ref byte pbSignature, int cbSignature, out int pcbResult, AsymmetricPaddingMode dwFlags);
private static extern ErrorCode NCryptSecretAgreement( SafeNCryptKeyHandle hPrivKey, SafeNCryptKeyHandle hPubKey, [Out] out SafeNCryptSecretHandle phSecret, int dwFlags);