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: David のアドバイスに基づいて、コードを次のように変更しました。

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;

以前と同じように、コンテンツに英語のみが含まれている場合に成功した結果が得られます。ただし、一部中国語コンテンツがある場合、「Illegal Sign」を通報した相手(Alipay)から拒否される結果となります。input_charset は「UTF8」として指定されます。ここで私が間違っていたことは他にありますか?ありがとうございました。

4

0 に答える 0