Я сгенерировал открытый ключ с использованием OpenSSL со следующими командами для создания 1024-битного ключа RSA, а затем экспортировал открытый ключ, предоставив мне открытый ключ в файле .pem.Как загрузить открытый открытый ключ открытого ключа RSA1024 OpenSSL с помощью CryptoAPI?
> openssl genrsa -out private_key.pem 1024
> openssl rsa -pubout -in private_key.pem -out public_key.pem
Я с помощью закрытого ключа, чтобы подписать какие-то данные в PHP, и хотите, чтобы проверить подпись в ОС Windows с помощью CryptoAPI. Я бы предпочел не связываться с портом Windows OpenSSL, если это возможно.
Открытый ключ в текстовом формате. Как я могу успешно загрузить эту версию и использовать ее для проверки подписи?
Текущий подход
Я пытаюсь использовать CryptImportKey
импортировать публичный ключ блоб с форматом:
- PUBLICKEYSTRUC
- Размер следующего массива байтов, DWORD
- Key как массив байтов (узкая/ANSI строка)
в упакованном виде cture.
Ключевой массив байтов - это просто файл открытого ключа .pem, включая операторы BEGIN и END, в виде строки ANSI (не Unicode). Обратите внимание, что я использую Unicode-приложение, но API-интерфейсы Crypto, похоже, не входят в версии 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;
Это должно создать макет в памяти, соответствующий примеру в Microsoft Importing a Plaintext Key example. Может быть. Действительно ли CALG_RSA_KEYX
подходит для RSA 1024-битного ключа (или, скорее, сгенерированного открытого ключа из одного)? Правильно ли используется значение Move()
? (Должен быть аналогом memcpy
, если вы программист C, но параметры являются источником, dest, size и строками 1-indexed, но массив индексируется 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 Crypto, и я уверен, что это что-то очевидно для других :)
Попробуйте Base64-декодирование ключа первым (открытый текст в этом контексте, я думаю, означает, что он не зашифрован, а не то, что он фактически находится в формате ASCII) –
@JonathanPotter Ах ... Я думаю, что это один из тех, «о, конечно. .. "моменты. Гах. Я попробую его и обновить :) –
Декодирование приводит к тому же коду ошибки в том же месте. Примечание. Мне нужно было отделить части BEGIN/END от ключа, чтобы его декодировать, и я не уверен, что это правильно - я думаю, что ключ открытого текста требует, чтобы они были частью его формата. –