2015-06-21 100 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部分才能對其進行解碼,但我不確定這是否正確 - 我認爲明文密鑰需要這些密鑰作爲其格式的一部分。 –

回答

相關問題