Я пытаюсь выполнить подписку и проверку SHA256 в Delphi с помощью OpenSSL libeay32.dll. Therefor в качестве первого шага я создал RSA 2048-битный ключ пары с помощью следующей команды OpenSSL:Проверка подписи SHA256 с OpenSSL в Delphi завершается
openssl genrsa -out private.pem 2048
openssl rsa -in private.pem -outform PEM -pubout -out public.pem
Это далеко, что легко. Следующий шаг я создавал функцию, которая была в состоянии прочитать открытые и закрытые ключи из файлов PEM:
function TSignSHA256.ReadKeyFile(aFileName : String; aType : TKeyFileType) : pEVP_PKEY;
var locFile : RawByteString;
locBIO : pBIO;
begin
locFile := UTF8Encode(aFileName);
locBIO := BIO_new(BIO_s_file());
try
BIO_read_filename(locBIO, PAnsiChar(locFile));
result := NIL;
case aType of
kfPrivate : result := PEM_read_bio_PrivateKey(locBIO, result, nil, nil);
kfPublic : result := PEM_read_bio_PUBKEY(locBIO, result, nil, nil);
end;
finally
BIO_free(locBIO);
end;
end;
Это, казалось, работать. Таким образом, я осуществил некоторые процедуры знак:
procedure TSignSHA256.Sign;
var locData : RawByteString;
locKey : pEVP_PKEY;
locCtx : pEVP_MD_CTX;
locSHA256 : pEVP_MD;
locSize : Cardinal;
locStream : TBytesStream;
begin
locKey := ReadKeyFile('private.pem', kfPrivate);
locData := ReadMessage('message.txt');
locCtx := EVP_MD_CTX_create;
try
locSHA256 := EVP_sha256();
EVP_DigestSignInit(locCtx, NIL, locSHA256, NIL, locKey);
EVP_DigestSignUpdate(locCtx, PAnsiChar(locData), Length(locData));
EVP_DigestSignFinal(locCtx, NIL, locSize);
locStream := TBytesStream.Create;
try
locStream.SetSize(locSize);
EVP_DigestSignFinal(locCtx, PAnsiChar(locStream.Memory), locSize);
WriteSignature('message.sig', locStream.Bytes, locSize);
finally
FreeAndNIL(locStream);
end;
finally
EVP_MD_CTX_destroy(locCtx);
end;
end;
Как вы можете видеть, что процедура читает файл с именем message.txt, вычисление подписи и хранения, что сиговых в message.sig. Если я запускаю следующую команду OpenSSL результат является Проверенно OK:
openssl dgst -sha256 -verify public.pem -signature message.sig message.txt
Таким образом, кажется, что моя процедура подписания также работает правильно. Так что я, наконец, реализовал процедуру верификации:
function TSignSHA256.Verify : Boolean;
var locData : RawByteString;
locSig : TArray<Byte>;
locKey : pEVP_PKEY;
locCtx : pEVP_MD_CTX;
locSHA256 : pEVP_MD;
locSize : Cardinal;
locStream : TBytesStream;
begin
locKey := ReadKeyFile('public.pem', kfPublic);
locData := ReadMessage('message.txt');
locSig := ReadSignature('message.sig');
locSize := Length(locSig);
locCtx := EVP_MD_CTX_create;
try
locSHA256 := EVP_sha256();
EVP_DigestVerifyInit(locCtx, NIL, EVP_sha256(), NIL, locKey); //Returns 1
EVP_DigestVerifyUpdate(locCtx, PAnsiChar(locData), Length(locData)); //Returns 1
locStream := TBytesStream.Create(locSig);
try
result := (EVP_DigestVerifyFinal(locCtx, PAnsiChar(locStream.Memory), locSize) = 1); //Returns false! WHY???
finally
FreeAndNIL(locStream);
end;
finally
EVP_MD_CTX_destroy(locCtx);
end;
end;
Как вы можете видеть, я реализовал эту процедуру точно так же, как и я осуществить процедуру подписания. К сожалению, результатом этого является false. Код ошибки, возвращаемый OpenSSL является
error04091077:lib(4):func(145):reason:(119)
Это приводит к ошибке в Lib RSA, функция int_rsa_verify, причина Неверная длина подписи. Я искал Google, но я не нашел полезной информации об этой ошибке. Я также попытался понять источники OpenSSL, но я не настолько глубоко в C, и кажется, что это может занять много времени, пока я не смогу понять это.
Мое личное ощущение, что я сделал что-то неправильно, прочитав открытый ключ. Но это только чувство, и я понятия не имею, как я могу это сделать по-другому. Мое второе предположение заключалось в том, что я сделал что-то не так, чтобы инициализировать контекст в процедуре проверки. Но я понятия не имею, что это может быть.
Почему проверка подписи не удалась?
Скучаешь обработки ошибок, начните с проверки, если '' EVP_DigestVerifyInit' и EVP_DigestVerifyUpdate' успеха (проверить возвращаемые значения) – Remko
См [ EVP Signing and Verification] (http://wiki.openssl.org/index.php/EVP_Signing_and_Verifying) в вики OpenSSL. Это дает вам примеры, которые работают из коробки. – jww
@Remko: Я просто оставил обработку ошибок для удобочитаемости. EVP_DigestVerifyInit и EVP_DigistVerifyUpdate возвращают 1, что означает успех. Я редактировал мой код, чтобы сделать это более понятным. –