我想在使用OpenSSL libeay32.dll的Delphi中實現SHA256簽名和驗證。爲此,在第一步驟我創建使用下面的OpenSSL一個RSA 2048位密鑰對命令:在Delphi中使用OpenSSL驗證SHA256簽名失敗
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文件,計算簽名並存儲到SIG 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簽名和驗證](http://wiki.openssl.org/index.php/EVP_Signing_and_Verifying)。它給你提供了一些可以開箱即用的例子。 – jww
@Remko:我只是將錯誤處理留給了可讀性。 EVP_DigestVerifyInit和EVP_DigistVerifyUpdate都返回1,這意味着成功。我編輯了我的代碼以使其更加清晰。 –