我用下面的命令生成了一個使用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
失敗並返回GetLastError
0x80090005
,NTE_BAD_DATA
。
我已經嘗試了很多變體 - 算法的一些變體,試驗大小,刪除鍵字符串的BEGIN和END部分等。但我不熟悉的加密API,我敢肯定這是別人:)
嘗試Base64解碼密鑰(在這種情況下明文,我認爲這意味着它沒有加密,而不是它實際上是ASCII格式) –
@JonathanPotter啊......我認爲那是「哦,當然。 ..「時刻。嘎。我會嘗試並更新:) –
解碼結果在相同的地方出現相同的錯誤代碼。注意我必須去掉密鑰的BEGIN/END部分才能對其進行解碼,但我不確定這是否正確 - 我認爲明文密鑰需要這些密鑰作爲其格式的一部分。 –