首页 > 解决方案 > 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)

我究竟做错了什么?

奖金阅读

完整的最小示例(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);
        }
    }
}

标签: windowscryptographycng

解决方案


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);

推荐阅读