2015-06-21 139 views
2

我用下面的命令生成了一个使用OpenSSL的公钥,并创建了一个1024位的RSA密钥,然后导出公钥给我一个.pem文件中的公钥。如何使用CryptoAPI加载OpenSSL生成的RSA1024明文公钥?

> openssl genrsa -out private_key.pem 1024 
> openssl rsa -pubout -in private_key.pem -out public_key.pem 

我使用的私钥签名在PHP中的一些数据,并且想使用的CryptoAPI在Windows验证签名。如果可能,我宁愿不链接到Windows OpenSSL端口。

公钥是文本格式。如何成功加载并使用它来验证签名?

目前的做法

我试图用CryptImportKey导入公共密钥块的格式:

  • PUBLICKEYSTRUC
  • 字节以下数组的大小,DWORD
  • 关键作为字节数组(窄/ ANSI字符串)

in a packed stru cture。

关键字节数组就是作为ANSI(非Unicode)字符串的公钥.pem文件的内容,包括BEGIN和END语句。请注意,我使用的是Unicode应用程序,但Crypto API似乎不是ANSI/Unicode版本。 (他们是吗?)

当我这样做时,CryptImportKey失败,GetLastError的值为0x80090005,NTE_BAD_DATA。我不知道这是为什么......这样的问题:)

公共密钥块结构被定义为

代码:

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; 

这应该创建一个与微软的Importing a Plaintext Key example中的示例相匹配的内存中布局。也许。是CALG_RSA_KEYX正确的RSA 1024位密钥(或者说,从一个生成的公钥)?我的使用Move()正确吗? (应该是memcpy的模拟,如果你是C程序员,但params是源代码,dest,size和字符串是1索引的,但是数组是0索引的。)如果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失败并返回GetLastError0x80090005NTE_BAD_DATA

我已经尝试了很多变体 - 算法的一些变体,试验大小,删除键字符串的BEGIN和END部分等。但我不熟悉的加密API,我敢肯定这是别人:)

+3

尝试Base64解码密钥(在这种情况下明文,我认为这意味着它没有加密,而不是它实际上是ASCII格式) –

+0

@JonathanPotter啊......我认为那是“哦,当然。 ..“时刻。嘎。我会尝试并更新:) –

+0

解码结果在相同的地方出现相同的错误代码。注意我必须去掉密钥的BEGIN/END部分才能对其进行解码,但我不确定这是否正确 - 我认为明文密钥需要这些密钥作为其格式的一部分。 –

回答

相关问题