You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求助:基于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 to UTF8String first: GenerateRSASignUTF8_JVCL(PrivateKey, UTF8String(MyUnicodeContent))

内容的提问来源于stack exchange,提问作者Justmade

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 06:38:56