windows - CryptoNG:使用 BCryptExportKey 导出 RSA 密钥失败并出现 STATUS_INVALID_HANDLE
问题描述
使用 Cryptography Next Generation API(又名 CryptoAPI Next Generation,又名 CryptoNG,又名 Cng,又名 BestCrypt,又名 bcrypt),我正在尝试导出新生成的 RSA 私钥对:
编辑:较短的代码版本:
BCRYPT_ALG_HANDLE alg;
BCryptOpenAlgorithmProvider(out alg, BCRYPT_RSA_ALGORITHM, null, 0);
BCRYPT_KEY_HANDLE key;
BCryptGenerateKeyPair(alg, out key, 4096, 0);
DWORD cbResult;
BCryptExportKey(key, 0, BCRYPT_RSAFULLPRIVATE_BLOB, null, 0, out cbResult, 0);
更长的代码版本:
NTSTATUS nt;
// Open the RSA algorithm provider
BCRYPT_ALG_HANDLE alg;
nt = BCryptOpenAlgorithmProvider(out alg, BCRYPT_RSA_ALGORITHM, null, 0);
NTStatusCheck(nt);
// Successfully opened the RSA algorithm provider
// Generate a 4096 bit RSA public-private key pair
BCRYPT_KEY_HANDLE key;
nt = BCryptGenerateKeyPair(alg, out key, 4096, 0);
NTStatusCheck(nt);
// Successfully generates a key pair (key <-- $4E737A0)
// Ask for the buffer size required to export the key pair
DWORD cbResult;
nt = BCryptExportKey(key, 0, BCRYPT_RSAFULLPRIVATE_BLOB, null, 0, out cbResult, 0);
NTStatusCheck(nt);
// Fails with 0xC0000008 (STATUS_INVALID_HANDLE)
我究竟做错了什么?
奖金阅读
- MSDN:BCryptGenerateKeyPair函数
- MSDN:BCryptExportKey函数
完整的最小示例(Delphi)
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils, Windows, ComObj, ActiveX;
type
NTSTATUS = Cardinal;
const
BCRYPT_RSA_ALGORITHM: WideString = 'RSA';
BCRYPT_RSAFULLPRIVATE_BLOB: WideString = 'RSAFULLPRIVATEBLOB';
BCRYPT_RSAPRIVATE_BLOB: WideString = 'RSAPRIVATEBLOB';
BCRYPT_RSAPUBLIC_BLOB: WideString = 'RSAPUBLICBLOB';
LEGACY_RSAPRIVATE_BLOB: WideString = 'CAPIPRIVATEBLOB';
LEGACY_RSAPUBLIC_BLOB: WideString = 'CAPIPUBLICBLOB';
function BCryptOpenAlgorithmProvider(out hAlgorithm: THandle; pszAlgId, pszImplementation: PWideChar; dwFlags: Cardinal): NTSTATUS; stdcall; external 'bcrypt.dll';
function BCryptGenerateKeyPair(hAlgorithm: THandle; out phKey: THandle; dwLength: Cardinal; dwFlags: Cardinal): NTSTATUS; stdcall; external 'bcrypt.dll';
function BCryptExportKey(hKey: THandle; hExportKey: THandle; pszBlobType: PWideChar; pbOutput: Pointer; cbOutput: Cardinal; out pcbResult: Cardinal; dwFlags: Cardinal): NTSTATUS; stdcall; external 'bcrypt.dll';
procedure Main;
var
nt: Cardinal;
alg: THandle;
key: THandle;
cbResult: Cardinal;
begin
// Open the RSA algorithm provider
WriteLn('Opening algorithm provider');
nt := BCryptOpenAlgorithmProvider({out} alg, PWideChar(BCRYPT_RSA_ALGORITHM), nil, 0);
OleCheck(HRESULT(nt));
// Generate a RSA public-private key pair
WriteLn('Generating key pair');
key := 0;
nt := BCryptGenerateKeyPair(alg, {out} key, 1024, 0);
OleCheck(HRESULT(nt));
// Ask for the buffer size required to export the key pair
WriteLn('Exporting full private blob');
cbResult := 0;
nt := BCryptExportKey(key, 0, PWideChar(BCRYPT_RSAFULLPRIVATE_BLOB), nil, 0, {out} cbResult, 0);
OleCheck(HRESULT(nt));
WriteLn('Success');
end;
begin
Main;
WriteLn('Press enter to close...');
ReadLn;
end.
完整的最小示例 (C#)
using System;
using System.Runtime.InteropServices;
namespace ConsoleApp1
{
class Program
{
[DllImport("bcrypt.dll", CharSet = CharSet.Unicode)]
internal static extern Int32 BCryptOpenAlgorithmProvider(out IntPtr phAlgorithm, [In] string pszAlgId, [In] string pszImplementation, [In] int dwFlags);
[DllImport("bcrypt.dll")]
internal static extern Int32 BCryptGenerateKeyPair([In] IntPtr hAlgorithm, out IntPtr phKey, [In] UInt32 dwLength, [In] UInt32 dwFlags);
[DllImport("bcrypt.dll", CharSet = CharSet.Unicode)]
internal static extern Int32 BCryptExportKey([In] IntPtr hKey, [In] IntPtr hExportKey, [MarshalAs(UnmanagedType.LPWStr)] [In] string pszBlobType, [MarshalAs(UnmanagedType.LPArray)] [Out] byte[] pbOutput, [In] int cbOutput, [In] ref UInt32 cbResult, [In] int dwFlags);
static void Main(string[] args)
{
Int32 nt;
IntPtr alg;
nt = BCryptOpenAlgorithmProvider(out alg, "RSA", null, 0);
if (nt < 0)
throw new COMException("Open algorithm", (int)nt);
IntPtr key;
nt = BCryptGenerateKeyPair(alg, out key, 1024, 0);
if (nt < 0)
throw new COMException("Generate key", nt);
UInt32 cbResult = 0;
nt = BCryptExportKey(key, IntPtr.Zero, "RSAPRIVATEBLOB", null, 0, ref cbResult, 0);
if (nt < 0)
throw new COMException("Export", nt);
}
}
}
解决方案
在BCryptGenerateKeyPair之后,您应该调用BCryptFinalizeKeyPair。在BCryptFinalizeKeyPair密钥对实际上并不存在之前。
使用此函数创建密钥后,[...] 在调用BCryptFinalizeKeyPair函数之前无法使用该密钥。
所以:
BCRYPT_ALG_HANDLE alg;
BCryptOpenAlgorithmProvider(out alg, BCRYPT_RSA_ALGORITHM, null, 0);
BCRYPT_KEY_HANDLE key;
BCryptGenerateKeyPair(alg, out key, 4096, 0);
BCryptFinalizeKeyPair(key, 0); //finalize the key so we can use it
DWORD cbResult;
BCryptExportKey(key, 0, BCRYPT_RSAFULLPRIVATE_BLOB, null, 0, out cbResult, 0);
推荐阅读
- docker - Docker 运行与拉取
- tensorflow - 如何使用 tf.Dataset 将数据加载到多个 GPU 中?
- vue.js - 在 Knockout js 视图模型中打开 Vue 组件
- c# - 为什么我必须读取传递给控制器的 Global.asax Application_BeginRequest 方法中的 inputStream?
- reactjs - 除非更新状态的方法运行两次,否则 React 组件无法访问新的状态值
- javascript - 使用 applescript 在 gmail 中自动打开链接
- powershell - 未存储简单的 powershell 变量以供稍后在脚本中使用
- python - 如果在另一个函数中调用,函数返回“none”
- python-3.x - 无法模拟数据库 Django 链查询
- git - 我必须在“git clone”之后运行“git pull”吗?