求助:基于JVCL与OpenSSL的Delphi UTF8字符串SHA1-RSA签名实现
Got it, let's tweak your existing AnsiString RSA-SHA1 signing code to support UTF-8 strings properly, using JVCL and OpenSSL. I'll break this down into practical steps and updated code since you're not super familiar with SSL internals.
Key Issues with the Original AnsiString Code
Your original code relies on AnsiString, which ties to the system's default ANSI encoding—this will corrupt UTF-8 characters that don't fit that encoding. For UTF-8 support, we need to:
- Work directly with UTF-8 byte streams instead of ANSI-encoded characters
- Ensure OpenSSL receives the correct byte length (not character count) of the UTF-8 content
- Leverage JVCL's built-in utilities to avoid reinventing the wheel (like Base64 encoding or RSA wrapper classes)
Updated Code: Manual OpenSSL Call (Based on Your Original)
This modifies your existing approach to handle UTF-8, with proper resource cleanup and error checks:
function GenerateRSASignUTF8(PrivateKey: AnsiString; Content: UTF8String): AnsiString; var hIdCrypto: HMODULE; key: PBIO; r: PRSA; hash: array[0..SHA_DIGEST_LENGTH - 1] of Byte; rsa_out: array[0..2047] of Byte; // Fits 2048-bit RSA signatures (256 bytes) rsa_out_len: Cardinal; // OpenSSL function prototypes type TSSL_load_error_strings = procedure; TOpenSSL_add_all_algorithms = procedure; TBIO_new_mem_buf = function(buf: Pointer; len: Integer): PBIO; cdecl; TBIO_free = procedure(a: PBIO); cdecl; TRSA_new = function: PRSA; cdecl; TRSA_free = procedure(r: PRSA); cdecl; TPEM_read_bio_RSAPrivateKey = function(bp: PBIO; x: PRSA; cb: Pointer; u: Pointer): PRSA; cdecl; TSHA1 = function(d: PByte; n: Cardinal; md: PByte): PByte; cdecl; TRSA_sign = function(_type: LongInt; const m: PByte; m_length: Cardinal; sigret: PByte; siglen: PCardinal; rsa: PRSA): Integer; cdecl; var SSL_load_error_strings: TSSL_load_error_strings; OpenSSL_add_all_algorithms: TOpenSSL_add_all_algorithms; BIO_new_mem_buf: TBIO_new_mem_buf; BIO_free: TBIO_free; RSA_new: TRSA_new; RSA_free: TRSA_free; PEM_read_bio_RSAPrivateKey: TPEM_read_bio_RSAPrivateKey; SHA1: TSHA1; RSA_sign: TRSA_sign; begin Result := ''; // Load OpenSSL crypto library (adjust DLL name for your version: libcrypto-1_1.dll, etc.) hIdCrypto := LoadLibrary('libeay32.dll'); if hIdCrypto = 0 then Exit; try // Resolve required OpenSSL functions @SSL_load_error_strings := GetProcAddress(hIdCrypto, 'SSL_load_error_strings'); @OpenSSL_add_all_algorithms := GetProcAddress(hIdCrypto, 'OpenSSL_add_all_algorithms'); @BIO_new_mem_buf := GetProcAddress(hIdCrypto, 'BIO_new_mem_buf'); @BIO_free := GetProcAddress(hIdCrypto, 'BIO_free'); @RSA_new := GetProcAddress(hIdCrypto, 'RSA_new'); @RSA_free := GetProcAddress(hIdCrypto, 'RSA_free'); @PEM_read_bio_RSAPrivateKey := GetProcAddress(hIdCrypto, 'PEM_read_bio_RSAPrivateKey'); @SHA1 := GetProcAddress(hIdCrypto, 'SHA1'); @RSA_sign := GetProcAddress(hIdCrypto, 'RSA_sign'); if not Assigned(SSL_load_error_strings) or not Assigned(OpenSSL_add_all_algorithms) or not Assigned(BIO_new_mem_buf) or not Assigned(BIO_free) or not Assigned(RSA_new) or not Assigned(RSA_free) or not Assigned(PEM_read_bio_RSAPrivateKey) or not Assigned(SHA1) or not Assigned(RSA_sign) then Exit; // Initialize OpenSSL SSL_load_error_strings; OpenSSL_add_all_algorithms; // Load PEM-formatted private key key := BIO_new_mem_buf(PAnsiChar(PrivateKey), Length(PrivateKey)); if not Assigned(key) then Exit; try r := PEM_read_bio_RSAPrivateKey(key, nil, nil, nil); if not Assigned(r) then Exit; try // Calculate SHA1 hash of UTF-8 content (use byte data directly) SHA1(PByte(Content), Length(Content), @hash[0]); // Sign the hash with RSA (NID_sha1 maps to SHA1WithRSA algorithm) if RSA_sign(NID_sha1, @hash[0], SHA_DIGEST_LENGTH, @rsa_out[0], @rsa_out_len, r) = 1 then begin // Encode signature to Base64 using JVCL's built-in utility Result := TJclBase64.EncodeBytesToString(@rsa_out[0], rsa_out_len); end; finally RSA_free(r); end; finally BIO_free(key); end; finally FreeLibrary(hIdCrypto); end; end;
Simplified Version Using JVCL's RSA Wrapper
JVCL has a TJclRSA class that handles all the low-level OpenSSL boilerplate for you. This is much easier to maintain:
uses JclCryptography, JclHash, JclBase64; function GenerateRSASignUTF8_JVCL(PrivateKey: AnsiString; Content: UTF8String): AnsiString; var RSA: TJclRSA; Hash: TBytes; Signature: TBytes; begin Result := ''; RSA := TJclRSA.Create; try // Load PEM private key if not RSA.LoadFromPrivateKey(PrivateKey) then Exit; // Compute SHA1 hash of UTF-8 content Hash := THashSHA1.GetHashBytes(Content); // Sign the hash (sgfSHA1 specifies SHA1WithRSA) if RSA.SignHash(Hash, Signature, sgfSHA1) then begin // Encode signature to Base64 Result := TJclBase64.EncodeBytesToString(Signature); end; finally RSA.Free; end; end;
Critical Notes for Usage
- JVCL Dependencies: Make sure your project includes these JVCL units:
JclCryptography,JclHash,JclBase64 - OpenSSL DLLs: Use the correct 32/64-bit version of OpenSSL DLLs matching your Delphi build, and place them in your program's directory or system path
- Input Conversion: If your source string is
UnicodeString, convert it toUTF8Stringfirst:GenerateRSASignUTF8_JVCL(PrivateKey, UTF8String(MyUnicodeContent))
内容的提问来源于stack exchange,提问作者Justmade
相关产品推荐
相关产品推荐

