Tôi đang cố gắng thực hiện ký và xác minh SHA256 trong Delphi bằng cách sử dụng OpenSSL libeay32.dll. Trong bước đầu tiên, tôi đã tạo một cặp khóa RSA 2048-bit bằng cách sử dụng các lệnh OpenSSL sau:Xác minh chữ ký SHA256 với OpenSSL trong Delphi không thành công
openssl genrsa -out private.pem 2048
openssl rsa -in private.pem -outform PEM -pubout -out public.pem
Điều đó thật dễ dàng. Bước tiếp theo tôi đã làm là tạo một hàm có thể đọc khóa công khai và riêng tư từ tệp 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;
Điều đó dường như cũng hoạt động. Vì vậy, tôi thực hiện một số thủ tục dấu:
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;
Như bạn có thể thấy các thủ tục đang đọc một tập tin gọi là message.txt, tính toán chữ ký và lưu trữ sig đó để message.sig. Nếu tôi chạy sau OpenSSL lệnh kết quả là đã kích OK:
openssl dgst -sha256 -verify public.pem -signature message.sig message.txt
Vì vậy, nó có vẻ như thủ tục ký của tôi cũng đang làm việc đúng. Vì vậy, cuối cùng tôi đã triển khai quy trình xác minh:
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;
Như bạn có thể thấy tôi đã thực hiện thủ tục này chính xác như cách tôi đã thực hiện quy trình ký. Rất tiếc, kết quả của việc này là false. Các mã lỗi trả về bởi OpenSSL là
error04091077:lib(4):func(145):reason:(119)
Đó dịch để một lỗi trong lib RSA, hoạt int_rsa_verify, lý do sai chiều dài chữ ký. Tôi đã tìm kiếm trên Google nhưng tôi không tìm thấy bất kỳ thông tin hữu ích nào về lỗi đó. Tôi cũng đã cố gắng để hiểu các nguồn OpenSSL, nhưng tôi không phải là sâu vào C và có vẻ như nó có thể mất lứa tuổi cho đến khi tôi có thể tìm ra nó.
Cảm giác cá nhân của tôi là tôi đã đọc sai khóa công khai. Nhưng đó chỉ là một cảm giác và tôi không có ý tưởng làm thế nào tôi có thể làm điều đó theo một cách khác. Dự đoán thứ hai của tôi là tôi đã làm điều gì đó sai trái trong bối cảnh trong quy trình xác minh. Nhưng tôi không biết điều đó có thể là gì.
Tại sao xác minh chữ ký không thành công?
Bạn bỏ lỡ xử lý lỗi, hãy bắt đầu với việc kiểm tra nếu 'EVP_DigestVerifyInit' và 'EVP_DigestVerifyUpdate' thành công (kiểm tra giá trị trả về) – Remko
Xem [ Đăng ký và xác minh EVP] (http://wiki.openssl.org/index.php/EVP_Signing_and_Verifying) trên wiki OpenSSL. Nó cung cấp cho bạn các ví dụ làm việc ra khỏi hộp. – jww
@Remko: Tôi vừa để lại lỗi xử lý để dễ đọc. EVP_DigestVerifyInit và EVP_DigistVerifyUpdate cả trả về 1 có nghĩa là thành công. Tôi đã chỉnh sửa mã của mình để làm rõ hơn. –