Delphi 如何使用CryptoAPI加载OpenSSL生成的RSA1024明文公钥?

Delphi 如何使用CryptoAPI加载OpenSSL生成的RSA1024明文公钥?,delphi,winapi,cryptography,cryptoapi,Delphi,Winapi,Cryptography,Cryptoapi,我使用OpenSSL和以下命令生成了一个公钥,以创建1024位RSA密钥,然后导出该公钥,并将该公钥保存在.pem文件中 > openssl genrsa -out private_key.pem 1024 > openssl rsa -pubout -in private_key.pem -out public_key.pem 我在PHP中使用私钥对一些数据进行签名,并希望在Windows中使用CryptoAPI验证签名。如果可能的话,我宁愿不链接到Windows OpenSSL

我使用OpenSSL和以下命令生成了一个公钥,以创建1024位RSA密钥,然后导出该公钥,并将该公钥保存在.pem文件中

> openssl genrsa -out private_key.pem 1024
> openssl rsa -pubout -in private_key.pem -out public_key.pem
我在PHP中使用私钥对一些数据进行签名,并希望在Windows中使用CryptoAPI验证签名。如果可能的话,我宁愿不链接到Windows OpenSSL端口

公钥为文本格式。如何成功加载此文件并使用它验证签名

当前方法 我正在尝试使用
CryptImportKey
导入公钥blob,格式如下:

  • 公共密钥结构
  • 以下字节数组的大小,DWORD
  • 作为字节数组的键(窄/ANSI字符串)
在压缩结构中

密钥字节数组只是公钥.pem文件的内容,包括开始和结束语句,作为ANSI(非Unicode)字符串。请注意,我使用的是Unicode应用程序,但加密API似乎没有ANSI/Unicode版本。(是吗?)

执行此操作时,
CryptImportKey
失败,值为
0x80090005
NTE\u BAD\u DATA
。我不知道为什么这是。。。因此,问题是:)

代码 公钥blob结构定义为:

TPublicKeyBlob = packed record
  strict private
    FHeader : BLOBHEADER;
    FKeyLength : DWORD;
    FKey : array[0..4095] of Byte;
  public
    procedure Init(const KeyStr : string);
    function Size : DWORD;
  end;
方法有:

procedure TPublicKeyBlob.Init(const KeyStr: string);
var
  KeyAnsi : AnsiString;
begin
  KeyAnsi := AnsiString(KeyStr);
  if Length(KeyAnsi) > Length(FKey) then
    raise Exception.Create('Key too long');

  FHeader.bType := PLAINTEXTKEYBLOB;
  FHeader.bVersion := CUR_BLOB_VERSION;
  FHeader.reserved := 0;
  FHeader.aiKeyAlg := CALG_RSA_KEYX;

  FKeyLength := Length(KeyAnsi) * sizeof(KeyAnsi[1]);
  assert(sizeof(KeyAnsi[1]) = 1); // Should be single byte strings!

  Move(KeyAnsi[1], FKey[0], Length(KeyAnsi) * sizeof(KeyAnsi[1]));
end;

function TPublicKeyBlob.Size: DWORD;
begin
  // Return only the used size - not all the byte array will be used
  Result := SizeOf(FHeader) + Sizeof(FKeyLength) + FKeyLength;
end;
这将创建一个内存中的布局,与Microsoft的中的示例相匹配。大概
CALG\u RSA\u KEYX
对于RSA 1024位密钥(或者更确切地说,从一个密钥生成的公钥)是否正确?我使用的
Move()
正确吗?(如果您是C程序员,但是参数是source、dest、size,字符串是1索引的,但是数组是0索引的,那么应该类似于
memcpy
。)size()应该返回整个结构的大小,还是只返回键数组

参数是一个字符串形式的公钥(如下所示)

然后按如下方式使用:

function TLicenseInfo.VerifySignature: Boolean;
const
  PublicKeyStr = '-----BEGIN PUBLIC KEY-----' +
    'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCeyh3x8qbGgq+ZlXm4erhjFMKY' +
    'RGhAyFc8DUupkaLlSSzXDcHHC27VtuxAKtAVvm97OGJMbdbtAUy6rmVH0GSQmP+0' +
    'Mct+7ncQRfpXJ4kAYg3gpv4dSsBl83rWDuQ06QDPjIT7eMdNMuUTm11GIYFnvyv4' +
    'C9Vn92hBC2QeRRlslwIDAQAB' +
    '-----END PUBLIC KEY-----';
var
  hCryptProv, hHash: wcrypt2.HCRYPTPROV;
  hKey: Cardinal;
  PublicKeyBlob : TPublicKeyBlob;
begin
  Result := False;

  PublicKeyBlob.Init(PublicKeyStr);

  if not CryptAcquireContext(@hCryptProv, 'Test', nil, PROV_RSA_FULL, 0) then
    if not CryptAcquireContext(@hCryptProv, 'Test', nil, PROV_RSA_FULL, CRYPT_NEWKEYSET) then
      Exit;

  try
    if CryptImportKey(hCryptProv, @PublicKeyBlob, PublicKeyBlob.Size, 0, 0, @hKey) then
    // ^ this is the line that fails
    begin
      // ... check the signature.
    end;
  finally
    CryptReleaseContext(hCryptProv, 0);
  end;
end;
标记行调用
CryptImportKey
失败,并且
GetLastError
返回
0x80090005
NTE\u BAD\u数据


我尝试了很多变体——算法的一些变体,尝试了大小,删除了密钥字符串的开始和结束部分,等等。但我不熟悉加密API,我相信这对其他人来说是显而易见的:)

我在很多年前(2012年)回答了一个类似的问题:

基本上,您必须使用get来解码BASE64值,然后使用and。 显示如何执行此操作的C示例位于。将其转换为delphi并不太困难


我希望这会有所帮助。

首先尝试对密钥进行Base64解码(在这种情况下,我认为明文意味着它没有加密,而不是实际上是ASCII格式)@JonathanPotter Ah。。。我想那是一个“哦,当然……”的时刻。嘎。我将尝试并更新:)解码会在同一位置产生相同的错误代码。注:为了解码,我必须去掉密钥的开始/结束部分,我不确定这是否正确-我认为明文密钥需要这些部分作为其格式的一部分。