0

我需要使用 RSA 进行签名,但我在 SSL 方面很弱,所以我在互联网上找到了一些代码。

我找到了以下代码,可用于成功签署 ansistring:

function GenarateRSASign(PrivateKey, Content: AnsiString): AnsiString;
var
  buffer: PAnsiChar;
  rsa_out, md: array[0..1023] of AnsiChar;
  r: PRSA;
  rsa_out_len: Cardinal;
  key: PBIO;
  hIdCrypto: HMODULE;
  hash: array[0..SHA_DIGEST_LENGTH - 1] of AnsiChar;
type
  Trsa_sign = function(_type: LongInt; const m: PAnsiChar; m_length: LongWord; sigret: PAnsiChar; var siglen: Cardinal; const rsa: PRSA): LongInt; cdecl;
  Tsha1 = function(d: PAnsiChar; n: Cardinal; md: PAnsiChar): PAnsiChar; cdecl;

  function LoadFunctionCLib(const FceName: string; const ACritical: Boolean = True): Pointer;
  begin
    Result := GetProcAddress(hIdCrypto, PChar(FceName));
  end;
var
  rsa_sign: Trsa_sign;
  sha1: Tsha1;
  bytes: TIdBytes;
begin
  LoadOpenSSLLibrary;
  hIdCrypto := LoadLibrary('libeay32.dll');
  Assert(hIdCrypto <> 0, 'Cannot load libeay32.dll');
  try
    key := BIO_new_mem_buf(@PrivateKey[1], Length(PrivateKey));
    r := PEM_read_bio_RSAPrivateKey(key, nil, nil, nil);
    if r = nil then
      Exit;
    buffer := PAnsiChar(Content);
    FillChar(md[0], 1024, 0);
    FillChar(hash[0], SHA_DIGEST_LENGTH, 0);
    sha1 := LoadFunctionCLib('SHA1');
    Assert(@sha1 <> nil, 'Cannot load SHA1');
    sha1(buffer, Length(Content), @hash[0]);
    rsa_sign := LoadFunctionCLib('RSA_sign');
    Assert(@rsa_sign <> nil, 'Cannot load RSA_sign');
    FillChar(rsa_out[0], 1024, 0);
    rsa_sign(EVP_sha1()._type, @hash[0], SHA_DIGEST_LENGTH, @rsa_out[0], rsa_out_len, r);
    SetLength(bytes, rsa_out_len);
    if rsa_out_len > 0 then
      Move(rsa_out[0], bytes[0], rsa_out_len);
    Result := AnsiString(TIdEncoderMIME.EncodeBytes(bytes));
    BIO_free(key);
    RSA_free(r);
  finally
    FreeLibrary(hIdCrypto);
    UnLoadOpenSSLLibrary;
  end;
end;

但是,我需要签署 utf8 字符串 因为我不知道 libeay32.dll 中是否有宽字符串版本的 'rsa_sign' 和 'sha1' 函数及其名称,我只是不知道如何更改代码以适应utf8 字符串签名。

我想对如何修改这些代码或有其他替代方法来做同样的工作提出一些建议。谢谢你。

编辑1:根据大卫的建议,我修改了代码如下:

function GenarateRSASign(PrivateKey: AnsiString, Content: String): String;
var
  buffer : TBytes;
  rsa_out, md: array[0..1023] of Byte;
  r: PRSA;
  rsa_out_len: Cardinal;
  key: PBIO;
  hIdCrypto: HMODULE;
  hash: array[0..SHA_DIGEST_LENGTH - 1] of Byte;
type
  Trsa_sign = function(_type: LongInt; const m: PBype; m_length: LongWord; sigret: PBype; var siglen: Cardinal; const rsa: PRSA): LongInt; cdecl;
  Tsha1 = function(d: PBype; n: Cardinal; md: PBype): PByte; cdecl;

  function LoadFunctionCLib(const FceName: string; const ACritical: Boolean = True): Pointer;
  begin
    Result := GetProcAddress(hIdCrypto, PChar(FceName));
  end;
var
  rsa_sign: Trsa_sign;
  sha1: Tsha1;
  bytes: TIdBytes;
begin
  LoadOpenSSLLibrary;
  hIdCrypto := LoadLibrary('libeay32.dll');
  Assert(hIdCrypto <> 0, 'Cannot load libeay32.dll');
  try
    key := BIO_new_mem_buf(@PrivateKey[1], Length(PrivateKey));
    r := PEM_read_bio_RSAPrivateKey(key, nil, nil, nil);
    if r = nil then
      Exit;
    FillChar(md[0], 1024, 0);
    FillChar(hash[0], SHA_DIGEST_LENGTH, 0);
    sha1 := LoadFunctionCLib('SHA1');
    Assert(@sha1 <> nil, 'Cannot load SHA1');
    buffer := TEncoding.UTF8.GetBytes(Content);
    sha1(@buffer[0], Length(Buffer), @hash[0]);
    rsa_sign := LoadFunctionCLib('RSA_sign');
    Assert(@rsa_sign <> nil, 'Cannot load RSA_sign');
    FillChar(rsa_out[0], 1024, 0);
    rsa_sign(EVP_sha1()._type, @hash[0], SHA_DIGEST_LENGTH, @rsa_out[0], rsa_out_len, r);
    SetLength(bytes, rsa_out_len);
    if rsa_out_len > 0 then
      Move(rsa_out[0], bytes[0], rsa_out_len);
    Result := TIdEncoderMIME.EncodeBytes(bytes);
    BIO_free(key);
    RSA_free(r);
  finally
    FreeLibrary(hIdCrypto);
    UnLoadOpenSSLLibrary;
  end;
end;

当内容只包含英语时,我得到了成功的结果,这和以前一样。但是,当有一些中文内容时,结果被对方(支付宝)报告“非法签名”拒绝。input_charset 被指定为“UTF8”。还有什么我在这里做错了吗?谢谢你。

4

0 回答 0