private static unsafe void ImportEncryptedPkcs8Overwrite( byte[] encryptedPkcs8, string keyName, SafeNCryptProviderHandle provHandle, bool machineScope, string password) { SafeNCryptKeyHandle keyHandle; fixed(char *passwordPtr = password) fixed(char *keyNamePtr = keyName) fixed(byte *blobPtr = encryptedPkcs8) { NativeMethods.NCrypt.NCryptBuffer *buffers = stackalloc NativeMethods.NCrypt.NCryptBuffer[2]; buffers[0] = new NativeMethods.NCrypt.NCryptBuffer { BufferType = NativeMethods.NCrypt.BufferType.PkcsSecret, cbBuffer = checked (2 * (password.Length + 1)), pvBuffer = new IntPtr(passwordPtr), }; if (buffers[0].pvBuffer == IntPtr.Zero) { buffers[0].cbBuffer = 0; } buffers[1] = new NativeMethods.NCrypt.NCryptBuffer { BufferType = NativeMethods.NCrypt.BufferType.PkcsName, cbBuffer = checked (2 * (keyName.Length + 1)), pvBuffer = new IntPtr(keyNamePtr), }; NativeMethods.NCrypt.NCryptBufferDesc desc = new NativeMethods.NCrypt.NCryptBufferDesc { cBuffers = 2, pBuffers = (IntPtr)buffers, ulVersion = 0, }; NativeMethods.NCrypt.NCryptImportFlags flags = NativeMethods.NCrypt.NCryptImportFlags.NCRYPT_OVERWRITE_KEY_FLAG | NativeMethods.NCrypt.NCryptImportFlags.NCRYPT_DO_NOT_FINALIZE_FLAG; if (machineScope) { flags |= NativeMethods.NCrypt.NCryptImportFlags.NCRYPT_MACHINE_KEY_FLAG; } int errorCode = NativeMethods.NCrypt.NCryptImportKey( provHandle, IntPtr.Zero, NCRYPT_PKCS8_PRIVATE_KEY_BLOB, ref desc, out keyHandle, new IntPtr(blobPtr), encryptedPkcs8.Length, flags); if (errorCode != 0) { keyHandle.Dispose(); throw new Win32Exception(errorCode); } using (keyHandle) using (CngKey cngKey = CngKey.Open(keyHandle, CngKeyHandleOpenOptions.None)) { const CngExportPolicies desiredPolicies = CngExportPolicies.AllowExport | CngExportPolicies.AllowPlaintextExport; cngKey.SetProperty( new CngProperty( "Export Policy", BitConverter.GetBytes((int)desiredPolicies), CngPropertyOptions.Persist)); int error = NativeMethods.NCrypt.NCryptFinalizeKey(keyHandle, 0); if (error != 0) { throw new Win32Exception(error); } } } }
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); } } }