署名に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」として指定されます。ここで私が間違っていたことは他にありますか?ありがとうございました。