Kai*_* T. 5 delphi openssl rsa digital-signature
我正在尝试使用OpenSSL libeay32.dll在Delphi中实现SHA256签名和验证.因此,在第一步中,我使用以下OpenSSL命令创建了一个RSA 2048位密钥对:
openssl genrsa -out private.pem 2048
openssl rsa -in private.pem -outform PEM -pubout -out public.pem
Run Code Online (Sandbox Code Playgroud)
那么容易.我做的下一步是创建一个能够从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;
Run Code Online (Sandbox Code Playgroud)
这似乎也有效.所以我实施了一些签名程序:
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;
Run Code Online (Sandbox Code Playgroud)
如您所见,该过程正在读取名为message.txt的文件,计算签名并将该sig存储到message.sig.如果我运行以下OpenSSL命令,结果是验证正常:
openssl dgst -sha256 -verify public.pem -signature message.sig message.txt
Run Code Online (Sandbox Code Playgroud)
所以我的签名程序似乎也正常.所以我最终实施了一个验证程序:
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;
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,我实现此过程的方式与实现签名过程完全相同.不幸的是,结果是错误的.OpenSSL返回的错误代码是
error04091077:lib(4):func(145):reason:(119)
Run Code Online (Sandbox Code Playgroud)
这转换为lib RSA中的错误,函数int_rsa_verify,原因是签名长度错误.我搜索了谷歌,但我没有找到任何关于该错误的有用信息.我也试图了解OpenSSL的来源,但我不是那么深入C,看起来它可能需要很长时间,直到我能够弄明白.
我个人的感觉是我在阅读公钥时做错了什么.但这只是一种感觉,我不知道如何以不同的方式做到这一点.我的第二个猜测是我在验证过程中对上下文做了一些错误.但我不知道那可能是什么.
为什么签名验证失败?
签名不是文本签名。它由一个字节数组组成,其中字节可以具有任何值。您正在将该字节数组直接与 ANSI 字符串相互转换。如果数组包含 ANSI 范围之外的值(无论是什么,我假设是 ASCII),则该操作将会失败。
您需要将签名视为二进制数据。如果需要将其视为字符串(包含文本),则可以使用 Base 64 编解码器。
| 归档时间: |
|
| 查看次数: |
1866 次 |
| 最近记录: |