public static CngKey ToECDHCngKey(this byte[] publicKey) { var key = publicKey.Length == 64 ? ByteHelper.Combine(P256PublicMagic, publicKey) : publicKey; return(CngKey.Import(key, CngKeyBlobFormat.EccPublicBlob)); }
public static unsafe byte[] ForceExport(this CngKey key, CngKeyBlobFormat format) { if ((key.ExportPolicy & CngExportPolicies.AllowPlaintextExport) != 0) { return(key.Export(format)); } // The key is not exportable, lets hack. Thanks @bartonjs // https://stackoverflow.com/questions/57269726/x509certificate2-import-with-ncrypt-allow-plaintext-export-flag // https://stackoverflow.com/questions/55236230/export-private-key-pkcs8-of-cng-rsa-certificate-with-oldschool-net string blobType = "PKCS8_PRIVATEKEY"; try { byte[] exported; fixed(byte *oidPtr = CryptNativeHelpers.PKCS12_3DES_OID) { var salt = ByteHelper.GetRandom(CryptNativeHelpers.NCrypt.PbeParams.RgbSaltSize); var pbeParams = new CryptNativeHelpers.NCrypt.PbeParams(); pbeParams.Params.iIterations = 1; pbeParams.Params.cbSalt = salt.Length; Marshal.Copy(salt, 0, (IntPtr)pbeParams.rgbSalt, salt.Length); var buffers = stackalloc CryptNativeHelpers.NCrypt.NCryptBuffer[3]; buffers[0] = new CryptNativeHelpers.NCrypt.NCryptBuffer { BufferType = CryptNativeHelpers.NCrypt.BufferType.PkcsSecret, cbBuffer = 0, pvBuffer = IntPtr.Zero, }; buffers[1] = new CryptNativeHelpers.NCrypt.NCryptBuffer { BufferType = CryptNativeHelpers.NCrypt.BufferType.PkcsAlgOid, cbBuffer = CryptNativeHelpers.PKCS12_3DES_OID.Length, pvBuffer = (IntPtr)oidPtr, }; buffers[2] = new CryptNativeHelpers.NCrypt.NCryptBuffer { BufferType = CryptNativeHelpers.NCrypt.BufferType.PkcsAlgParam, cbBuffer = sizeof(CryptNativeHelpers.NCrypt.PbeParams), pvBuffer = (IntPtr)(&pbeParams), }; var desc = new CryptNativeHelpers.NCrypt.NCryptBufferDesc { cBuffers = 3, pBuffers = (IntPtr)buffers, ulVersion = 0, }; if (CryptNativeHelpers.NCrypt.NCryptExportKey(key.Handle, IntPtr.Zero, blobType, ref desc, null, 0, out int bytesNeeded, 0) != 0) { return(null); } exported = new byte[bytesNeeded]; if (CryptNativeHelpers.NCrypt.NCryptExportKey(key.Handle, IntPtr.Zero, blobType, ref desc, exported, exported.Length, out bytesNeeded, 0) != 0) { return(null); } } fixed(char *keyNamePtr = key.KeyName) fixed(byte *blobPtr = exported) { var buffers = stackalloc CryptNativeHelpers.NCrypt.NCryptBuffer[2]; buffers[0] = new CryptNativeHelpers.NCrypt.NCryptBuffer { BufferType = CryptNativeHelpers.NCrypt.BufferType.PkcsSecret, cbBuffer = 0, pvBuffer = IntPtr.Zero, }; buffers[1] = new CryptNativeHelpers.NCrypt.NCryptBuffer { BufferType = CryptNativeHelpers.NCrypt.BufferType.PkcsName, cbBuffer = checked (2 * (key.KeyName.Length + 1)), pvBuffer = new IntPtr(keyNamePtr), }; var desc = new CryptNativeHelpers.NCrypt.NCryptBufferDesc { cBuffers = 2, pBuffers = (IntPtr)buffers, ulVersion = 0, }; SafeNCryptKeyHandle keyHandle; if (CryptNativeHelpers.NCrypt.NCryptImportKey(key.ProviderHandle, IntPtr.Zero, blobType, ref desc, out keyHandle, new IntPtr(blobPtr), exported.Length, CryptNativeHelpers.NCrypt.NCryptImportFlags.NCRYPT_OVERWRITE_KEY_FLAG | CryptNativeHelpers.NCrypt.NCryptImportFlags.NCRYPT_DO_NOT_FINALIZE_FLAG) != 0) { keyHandle.Dispose(); return(null); } using (keyHandle) using (CngKey cngKey = CngKey.Open(keyHandle, CngKeyHandleOpenOptions.None)) { cngKey.SetProperty(new CngProperty("Export Policy", BitConverter.GetBytes((int)CngExportPolicies.AllowPlaintextExport), CngPropertyOptions.Persist)); if (CryptNativeHelpers.NCrypt.NCryptFinalizeKey(keyHandle, 0) != 0) { return(null); } return(cngKey.Export(format)); } } } catch (Exception) { return(null); } }