private static unsafe byte[] ExportPkcs8KeyBlob( SafeNCryptKeyHandle keyHandle, string password, int kdfCount) { var pbeParams = new NativeMethods.NCrypt.PbeParams(); NativeMethods.NCrypt.PbeParams *pbeParamsPtr = &pbeParams; var salt = new byte[NativeMethods.NCrypt.PbeParams.RgbSaltSize]; RandomNumberGenerator.GetBytes(salt); pbeParams.Params.cbSalt = salt.Length; Marshal.Copy(salt, 0, (IntPtr)pbeParams.rgbSalt, salt.Length); pbeParams.Params.iIterations = kdfCount; var keyName = Guid.NewGuid().ToString("D"); fixed(char *passwordPtr = password) fixed(char *keyNamePtr = keyName) fixed(byte *oidPtr = s_pkcs12TripleDesOidBytes) { NativeMethods.NCrypt.NCryptBuffer *buffers = stackalloc NativeMethods.NCrypt.NCryptBuffer[3]; buffers[0] = new NativeMethods.NCrypt.NCryptBuffer { BufferType = NativeMethods.NCrypt.BufferType.PkcsSecret, cbBuffer = checked (2 * (password.Length + 1)), pvBuffer = (IntPtr)passwordPtr, }; if (buffers[0].pvBuffer == IntPtr.Zero) { buffers[0].cbBuffer = 0; } buffers[1] = new NativeMethods.NCrypt.NCryptBuffer { BufferType = NativeMethods.NCrypt.BufferType.PkcsAlgOid, cbBuffer = s_pkcs12TripleDesOidBytes.Length, pvBuffer = (IntPtr)oidPtr, }; buffers[2] = new NativeMethods.NCrypt.NCryptBuffer { BufferType = NativeMethods.NCrypt.BufferType.PkcsAlgParam, cbBuffer = sizeof(NativeMethods.NCrypt.PbeParams), pvBuffer = (IntPtr)pbeParamsPtr, }; var desc = new NativeMethods.NCrypt.NCryptBufferDesc { cBuffers = 3, pBuffers = (IntPtr)buffers, ulVersion = 0, }; int result = NativeMethods.NCrypt.NCryptExportKey( keyHandle, IntPtr.Zero, NCRYPT_PKCS8_PRIVATE_KEY_BLOB, ref desc, null, 0, out int bytesNeeded, 0); if (result != 0) { throw new Win32Exception(result); } byte[] exported = new byte[bytesNeeded]; result = NativeMethods.NCrypt.NCryptExportKey( keyHandle, IntPtr.Zero, NCRYPT_PKCS8_PRIVATE_KEY_BLOB, ref desc, exported, exported.Length, out bytesNeeded, 0); if (result != 0) { throw new Win32Exception(result); } if (bytesNeeded != exported.Length) { Array.Resize(ref exported, bytesNeeded); } NativeMethods.NCrypt.NCryptOpenStorageProvider( out var safeNCryptProviderHandle, MicrosoftSoftwareKeyStorageProvider, 0); NativeMethods.NCrypt.NCryptBuffer *buffers2 = stackalloc NativeMethods.NCrypt.NCryptBuffer[4]; buffers2[0] = new NativeMethods.NCrypt.NCryptBuffer { BufferType = NativeMethods.NCrypt.BufferType.PkcsSecret, cbBuffer = checked (2 * (password.Length + 1)), pvBuffer = (IntPtr)passwordPtr, }; if (buffers2[0].pvBuffer == IntPtr.Zero) { buffers2[0].cbBuffer = 0; } buffers2[1] = new NativeMethods.NCrypt.NCryptBuffer { BufferType = NativeMethods.NCrypt.BufferType.PkcsAlgOid, cbBuffer = s_pkcs12TripleDesOidBytes.Length, pvBuffer = (IntPtr)oidPtr, }; buffers2[2] = new NativeMethods.NCrypt.NCryptBuffer { BufferType = NativeMethods.NCrypt.BufferType.PkcsAlgParam, cbBuffer = sizeof(NativeMethods.NCrypt.PbeParams), pvBuffer = (IntPtr)pbeParamsPtr, }; buffers2[3] = new NativeMethods.NCrypt.NCryptBuffer { BufferType = NativeMethods.NCrypt.BufferType.PkcsKeyName, cbBuffer = checked (2 * (keyName.Length + 1)), pvBuffer = (IntPtr)keyNamePtr, }; var desc2 = new NativeMethods.NCrypt.NCryptBufferDesc { cBuffers = 4, pBuffers = (IntPtr)buffers2, ulVersion = 0, }; result = NativeMethods.NCrypt.NCryptImportKey( safeNCryptProviderHandle, IntPtr.Zero, NCRYPT_PKCS8_PRIVATE_KEY_BLOB, ref desc2, out var safeNCryptKeyHandle, exported, exported.Length, NCRYPT_DO_NOT_FINALIZE_FLAG); if (result != 0) { throw new Win32Exception(result); } var exportPolicyBytes = BitConverter.GetBytes( (int)(CngExportPolicies.AllowExport | CngExportPolicies.AllowPlaintextExport | CngExportPolicies.AllowArchiving | CngExportPolicies.AllowPlaintextArchiving)); NativeMethods.NCrypt.NCryptSetProperty( safeNCryptKeyHandle, "Export Policy", exportPolicyBytes, exportPolicyBytes.Length, CngPropertyOptions.Persist); NativeMethods.NCrypt.NCryptFinalizeKey( safeNCryptKeyHandle, 0); using (var key2 = CngKey.Open(keyName)) { Trace.WriteLine(key2.ExportPolicy); var plainTextBytes = key2.Export(CngKeyBlobFormat.Pkcs8PrivateBlob); key2.Delete(); return(plainTextBytes); } } }
private static unsafe byte[] ExportEncryptedPkcs8( CngKey cngKey, string password, int kdfCount) { var pbeParams = new NativeMethods.NCrypt.PbeParams(); NativeMethods.NCrypt.PbeParams *pbeParamsPtr = &pbeParams; byte[] salt = new byte[NativeMethods.NCrypt.PbeParams.RgbSaltSize]; using (RandomNumberGenerator rng = RandomNumberGenerator.Create()) { rng.GetBytes(salt); } pbeParams.Params.cbSalt = salt.Length; Marshal.Copy(salt, 0, (IntPtr)pbeParams.rgbSalt, salt.Length); pbeParams.Params.iIterations = kdfCount; fixed(char *stringPtr = password) fixed(byte *oidPtr = s_pkcs12TripleDesOidBytes) { NativeMethods.NCrypt.NCryptBuffer *buffers = stackalloc NativeMethods.NCrypt.NCryptBuffer[3]; buffers[0] = new NativeMethods.NCrypt.NCryptBuffer { BufferType = NativeMethods.NCrypt.BufferType.PkcsSecret, cbBuffer = checked (2 * (password.Length + 1)), pvBuffer = (IntPtr)stringPtr, }; if (buffers[0].pvBuffer == IntPtr.Zero) { buffers[0].cbBuffer = 0; } buffers[1] = new NativeMethods.NCrypt.NCryptBuffer { BufferType = NativeMethods.NCrypt.BufferType.PkcsAlgOid, cbBuffer = s_pkcs12TripleDesOidBytes.Length, pvBuffer = (IntPtr)oidPtr, }; buffers[2] = new NativeMethods.NCrypt.NCryptBuffer { BufferType = NativeMethods.NCrypt.BufferType.PkcsAlgParam, cbBuffer = sizeof(NativeMethods.NCrypt.PbeParams), pvBuffer = (IntPtr)pbeParamsPtr, }; var desc = new NativeMethods.NCrypt.NCryptBufferDesc { cBuffers = 3, pBuffers = (IntPtr)buffers, ulVersion = 0, }; using (var keyHandle = cngKey.Handle) { int result = NativeMethods.NCrypt.NCryptExportKey( keyHandle, IntPtr.Zero, NCRYPT_PKCS8_PRIVATE_KEY_BLOB, ref desc, null, 0, out int bytesNeeded, 0); if (result != 0) { throw new Win32Exception(result); } byte[] exported = new byte[bytesNeeded]; result = NativeMethods.NCrypt.NCryptExportKey( keyHandle, IntPtr.Zero, NCRYPT_PKCS8_PRIVATE_KEY_BLOB, ref desc, exported, exported.Length, out bytesNeeded, 0); if (result != 0) { throw new Win32Exception(result); } if (bytesNeeded != exported.Length) { Array.Resize(ref exported, bytesNeeded); } return(exported); } } }