问题
I am sending down some information from a server that is using OpenSSL::Cipher to encrypt the data using AES-256-CBC. I am receiving the data in an application that is coded in Delphi XE8 and attempting to decrypt the data using TPLB 3 OpenSSL. From everything I have tried I have all of the information matching, the key, the iv etc), but I still get an error or junk data when attempting to decrypt. I am assuming that there is something I am missing with TPLB 3 setup/config to get it to decrypt properly, but I can't for the life of me figure it out. Any help is much appreciated.
Delphi Decrypting
function TLicenseReload.Decode(L, K, I: string): string;
var
cdec: TOpenSSL_Codec;
s: string;
sOut,
sIn: TStream;
begin
Result := '';
cdec := TOpenSSL_Codec.Create(nil);
sIn := TStringStream.Create;
sout := TStringStream.Create;
try
sIn.Write(L, length(L));
sIn.Position := 0;
cdec.SetKey(TEncoding.Default.GetBytes(K));
cdec.SetIV(TEncoding.Default.GetBytes(I));
cdec.Cipher := cipher_aes_256_cbc;
cdec.PaddingScheme := {padNone;//}padPKCS;
//cdec.LibName := 'libeay32.dll'; //toggled on and off to attempt to decrypt correctly
//cdec.LibPath := ExtractFilePath(Application.Exename); //toggled on and off to attempt to decrypt correctly
//cdec.RequiredVersion := '1.0.1.7'; //toggled on and off to attempt to decrypt correctly
cdec.isLoaded := true; //receive an access violation if this is not set
cdec.Decrypt(sOut, sIn);
//s := sOut.DataString; //was using TStringStream but wasn't working so switched to TStream
sOut.ReadBuffer(s[1], sOut.Size - sOut.Position);
result := s;
finally
sOut.Free;
sIn.Free;
cdec.Free;
end;
end;
Ruby Encrypting
begin
unless loc.nil?
cip = OpenSSL::Cipher.new('AES-256-CBC')
cip.encrypt
cip.key = Digest::SHA1.hexdigest(loc.l_hash[0..31].upcase).upcase
lic_iv = cip.random_iv
lic_iv = Base64.encode64(lic_iv)
enc_lic_date = cip.update(loc.licensed_through.to_s + ':' + loc.customer.purchased.to_s) + cip.final
enc_lic_date = Base64.encode64(enc_lic_date)#.encode('utf-8')
#enc_lic_date << cip.final
end
rescue StandardError => e
error_message = e.to_s
puts e.to_s
end
EDIT:
I went back and double checked everything (basically starting over). I have confirmed that the bytes being encrypted on the server (before they are Base64 encdoed) are the same as the bytes that are being decrypted (post Base64 decoding) on the client. However, I am still getting "junk" out.
Updated (cluttered) Delphi Decrypting
function TLicenseReload.DecodeLicense(L, K, I: string): string;
var
cdec: TOpenSSL_Codec;
s: string;
sOut,
sIn: TStringStream;
x,
y: TBytes;
z: string;
begin
Result := '';
cdec := TOpenSSL_Codec.Create(nil);
sIn := TStringStream.Create;
sout := TStringStream.Create;
try
SetLength(x, Length(K));
SetLength(y, Length(DecodeBase64(I)));
//SetLength(z, Length(DecodeBase64(L)));
x := TEncoding.UTF8.GetBytes(K);
y := DecodeBase64(I);
//z := string(DecodeBase64(L));
//sIn.WriteString(z);//, length(z));
sIn.WriteData(DecodeBase64(L), length(DecodeBase64(L)));
sIn.Position := 0;
//cdec.SetKey(TEncoding.UTF8.GetBytes(unbaseit(K)));
//cdec.SetIV(TEncoding.UTF8.GetBytes(unbaseit(I)));
cdec.SetKey(TEncoding.UTF8.GetBytes(K));
cdec.SetIV(DecodeBase64(I));
cdec.Cipher := cipher_aes_256_cbc;
cdec.PaddingScheme := padNone;//}padPKCS;
//cdec.LibName := 'libeay32.dll';
//cdec.LibPath := ExtractFilePath(Application.Exename);
//cdec.RequiredVersion := '1.0.1.7';
cdec.isLoaded := true;
cdec.Decrypt(sOut, sIn);
s := sOut.DataString;
//sOut.ReadBuffer(s[1], sOut.Size - sOut.Position);
result := s;
finally
sOut.Free;
sIn.Free;
cdec.Free;
end;
end;
EDIT 2 TPLB3 has two options for padding, None or PKCS. With None set, I get junk out. With PKCS set I get an "OpenSSL encryption error". The encoding on the results does not seem to matter, it is still junk.
回答1:
I was able to get the encrypted data sent down to decrypt. I ended up using the functions and unit from this post. I had to modify them slightly, and updated a couple of the function calls to fit my needs, which I will post below.
I had looked at the post before and could not get it to work and gave up and went back to TPLB3. After banging my head against a wall for hours and hours and researching and digging around on the net, I was so close with TPLB3 and I just could not get the expected results. So at a suggestion from a co-worker, I looked at this again and realized that when I tried it the first time I was indeed passing bytes from the wrong encoded strings, which I found and tracked down in my quest to get TPLB3 to work. Once I plugged this back in, it took a little tweaking and updating (the updating just in case the old functions were ever removed) to get it to work properly for my specific case.
SALT_MAGIC: AnsiString = 'Salted__';
SALT_MAGIC_LEN: integer = 8;
SALT_SIZE = 8;
function EVP_Decrypt_AES256(const Value: TBytes; APassword: TBytes; AIV: TBytes): TBytes;
var
cipher: PEVP_CIPHER;
ctx: EVP_CIPHER_CTX;
salt, key, iv, buf: TBytes;
src_start, buf_start, out_len, attempt: integer;
begin
cipher := EVP_aes_256_cbc;
SetLength(salt, SALT_SIZE);
// First read the magic text and the salt - if any
if (AnsiString(TEncoding.ASCII.GetString(Value, 0, SALT_MAGIC_LEN)) = SALT_MAGIC) then
begin
Move(Value[SALT_MAGIC_LEN], salt[0], SALT_SIZE);
EVP_GetKeyIV(APassword, cipher, salt, key, iv);
src_start := SALT_MAGIC_LEN + SALT_SIZE;
end
else
begin
//EVP_GetKeyIV(APassword, cipher, nil, key, iv); //commented out because we are passing in a known iv
key := APassword;
iv := AIV;
src_start := 0;
end;
EVP_CIPHER_CTX_init(@ctx);
try
EVP_DecryptInit_ex(@ctx, cipher, nil, @key[0], @iv[0]);
SetLength(buf, Length(Value));
buf_start := 0;
//EVP_DecryptUpdate(@ctx, @buf[buf_start], @out_len, @Value[src_start], Length(Value) - src_start);
EVP_DecryptUpdate(@ctx, @buf[buf_start], out_len, @Value[src_start], Length(Value) - src_start);
Inc(buf_start, out_len);
//EVP_DecryptFinal(@ctx, @buf[buf_start], @out_len);
EVP_DecryptFinal_ex(@ctx, @buf[buf_start], out_len);
Inc(buf_start, out_len);
if ((length(buf) > 0) and (buf_start > 0) and (buf[0] <> 0)) then
SetLength(buf, buf_start);
result := buf;
finally
repeat
until EVP_CIPHER_CTX_cleanup(@ctx) = 1;
end;
end;
function EVP_GetSalt: TBytes;
begin
SetLength(result, PKCS5_SALT_LEN);
RAND_pseudo_bytes(@result[0], PKCS5_SALT_LEN);
end;
procedure EVP_GetKeyIV(APassword: TBytes; ACipher: PEVP_CIPHER; const ASalt: TBytes; out Key, IV: TBytes);
var
ctx: EVP_MD_CTX;
hash: PEVP_MD;
mdbuff: TBytes;
mds: cardinal;
nkey, niv: integer;
begin
hash := EVP_sha256;
mds := 0;
SetLength(mdbuff, EVP_MAX_MD_SIZE);
nkey := ACipher.key_len;
niv := ACipher.iv_len;
SetLength(Key, nkey);
SetLength(IV, nkey); // Max size to start then reduce it at the end
Assert(hash.md_size >= nkey);
Assert(hash.md_size >= niv);
// This is pretty much the same way that EVP_BytesToKey works. But that
// allows multiple passes through the hashing loop and also allows to
// choose different hashing methods. We have no need for this. The
// OpenSSL docs say it is out of date and internet sources suggest using
// something like PKCS5_v2_PBE_keyivgen and/or PKCS5_PBKDF2_HMAC_SHA1
// but this method is easy to port to the DEC and DCP routines and easy to
// use in other environments. Ultimately the Key and IV rely on the password
// and the salt and can be easily reformed.
// This method relies on the fact that the hashing method produces a key of
// the correct size. EVP_BytesToKey goes through muptiple hashing passes if
// necessary to make the key big enough when using smaller hashes.
EVP_MD_CTX_init(@ctx);
try
// Key first
EVP_DigestInit_ex(@ctx, hash, nil);
EVP_DigestUpdate(@ctx, @APassword[0], Length(APassword));
if (ASalt <> nil) then
EVP_DigestUpdate(@ctx, @ASalt[0], Length(ASalt));
EVP_DigestFinal_ex(@ctx, @Key[0], mds);
// Derive IV next
EVP_DigestInit_ex(@ctx, hash, nil);
EVP_DigestUpdate(@ctx, @Key[0], mds);
EVP_DigestUpdate(@ctx, @APassword[0], Length(APassword));
if (ASalt <> nil) then
EVP_DigestUpdate(@ctx, @ASalt[0], Length(ASalt));
EVP_DigestFinal_ex(@ctx, @IV[0], mds);
SetLength(IV, niv);
finally
EVP_MD_CTX_cleanup(@ctx);
end;
end;
unit libeay32;
{
Provided by user shunty from StackOverflow
https://stackoverflow.com/questions/9723963/delphi-pascal-example-for-calling-openssl-evp-functions
Import unit for the OpenSSL libeay32.dll library.
Originally based on the work by Marco Ferrante.
http://www.csita.unige.it/
http://www.disi.unige.it/
then on the Indy libraries
and, of course, the C source code from http://www.openssl.org
Only the parts that we need to use have been translated/imported. There are
a whole load of functions in the library that aren't included here
2010-03-11 Why re-invent the wheel. Indy has done a large chunk of this
already so use it - IdSSLOpenSSLHeaders
Now we generally just include stuff that isn't available in the Indy code.
Primarily encryption stuff rather than SSL stuff.
}
interface
uses
SysUtils, Windows,
IdSSLOpenSSLHeaders;
const
LIBEAY_DLL_NAME = '.\OpenSSL\1_0_1l\libeay32.dll';
PROC_ADD_ALL_ALGORITHMS_NOCONF = 'OPENSSL_add_all_algorithms_noconf';
PROC_ADD_ALL_ALGORITHMS = 'OpenSSL_add_all_algorithms';
EVP_PKEY_RSA = IdSSLOpenSSLHeaders.EVP_PKEY_RSA;
PKCS5_SALT_LEN = IdSSLOpenSSLHeaders.PKCS5_SALT_LEN;
EVP_MAX_KEY_LENGTH = IdSSLOpenSSLHeaders.EVP_MAX_KEY_LENGTH;
EVP_MAX_IV_LENGTH = IdSSLOpenSSLHeaders.EVP_MAX_IV_LENGTH;
EVP_MAX_MD_SIZE = IdSSLOpenSSLHeaders.EVP_MAX_MD_SIZE;
type
PEVP_PKEY = IdSSLOpenSSLHeaders.PEVP_PKEY;
PRSA = IdSSLOpenSSLHeaders.PRSA;
EVP_MD_CTX = IdSSLOpenSSLHeaders.EVP_MD_CTX;
EVP_CIPHER_CTX = IdSSLOpenSSLHeaders.EVP_CIPHER_CTX;
PEVP_CIPHER = IdSSLOpenSSLHeaders.PEVP_CIPHER;
PEVP_MD = IdSSLOpenSSLHeaders.PEVP_MD;
type
TSSLProgressCallbackFunction = procedure (status: integer; value: integer; cb_arg: pointer);
TSSLPasswordCallbackFunction = function (buffer: TBytes; size: integer; rwflag: integer; u: pointer): integer; cdecl;
TOpenSSL_InitFunction = procedure; cdecl;
type
PEK_ARRAY = ^EK_ARRAY;
EK_ARRAY = array of PByteArray;
PUBK_ARRAY = array of PEVP_PKEY;
PPUBK_ARRAY = ^PUBK_ARRAY;
function EVP_aes_256_cbc: PEVP_CIPHER; cdecl;
function EVP_md5: PEVP_MD; cdecl;
function EVP_sha1: PEVP_MD; cdecl;
function EVP_sha256: PEVP_MD; cdecl;
function EVP_PKEY_assign(pkey: PEVP_PKEY; key_type: integer; key: Pointer): integer; cdecl;
function EVP_PKEY_new: PEVP_PKEY; cdecl;
procedure EVP_PKEY_free(key: PEVP_PKEY); cdecl;
function EVP_PKEY_assign_RSA(pkey: PEVP_PKEY; key: PRSA): integer;
function EVP_PKEY_size(pkey: PEVP_PKEY): integer; cdecl;
procedure EVP_CIPHER_CTX_init(a: PEVP_CIPHER_CTX); cdecl;
procedure EVP_CipherInit_ex(A: PEVP_CIPHER_CTX); cdecl;
function EVP_CIPHER_CTX_cleanup(a: PEVP_CIPHER_CTX): integer; cdecl;
function EVP_CIPHER_CTX_block_size(ctx: PEVP_CIPHER_CTX): integer; cdecl;
procedure EVP_MD_CTX_init(ctx: PEVP_MD_CTX); cdecl;
function EVP_MD_CTX_cleanup(ctx: PEVP_MD_CTX): integer; cdecl;
function EVP_BytesToKey(cipher_type: PEVP_CIPHER; md: PEVP_MD; salt: PByte; data: PByte; datal: integer; count: integer; key: PByte; iv: PByte): integer; cdecl;
function EVP_EncryptInit_ex(ctx: PEVP_CIPHER_CTX; cipher_type: PEVP_CIPHER; impl: PENGINE; key: PByte; iv: PByte): integer; cdecl;
function EVP_EncryptInit(ctx: PEVP_CIPHER_CTX; cipher_type: PEVP_CIPHER; key: PByte; iv: PByte): integer; cdecl;
function EVP_EncryptUpdate(ctx: PEVP_CIPHER_CTX; data_out: PByte; var outl: integer; data_in: PByte; inl: integer): integer; cdecl;
function EVP_EncryptFinal(ctx: PEVP_CIPHER_CTX; data_out: PByte; var outl: integer): integer; cdecl;
function EVP_DecryptInit_ex(ctx: PEVP_CIPHER_CTX; cipher_type: PEVP_CIPHER; impl: PENGINE; key: PByte; iv: PByte): integer; cdecl;
function EVP_DecryptInit(ctx: PEVP_CIPHER_CTX; cipher_type: PEVP_CIPHER; key: PByte; iv: PByte): integer; cdecl;
function EVP_DecryptUpdate(ctx: PEVP_CIPHER_CTX; data_out: PByte; var outl: integer; data_in: PByte; inl: integer): integer; cdecl;
function EVP_DecryptFinal(ctx: PEVP_CIPHER_CTX; data_out: PByte; var outl: integer): integer; cdecl;
function EVP_DecryptFinal_ex(ctx: PEVP_CIPHER_CTX; data_out: PByte; var outl: integer): integer; cdecl;
function EVP_SealInit(ctx: PEVP_CIPHER_CTX; cipher_type: PEVP_CIPHER; ek: PEK_ARRAY; ekl: PIntegerArray; iv: PByte; pubk: PPUBK_ARRAY; npubk: integer): integer; cdecl;
function EVP_SealUpdate(ctx: PEVP_CIPHER_CTX; data_out: PByte; var outl: integer; data_in: PByte; inl: integer): integer;
function EVP_SealFinal(ctx: PEVP_CIPHER_CTX; data_out: PByte; var outl: integer): integer; cdecl;
function EVP_OpenInit(ctx: PEVP_CIPHER_CTX; cipher_type: PEVP_CIPHER; ek: PByte; ekl: integer; iv: PByte; priv: PEVP_PKEY): integer; cdecl;
function EVP_OpenUpdate(ctx: PEVP_CIPHER_CTX; data_out: PByte; var outl: integer; data_in: PByte; inl: integer): integer;
function EVP_OpenFinal(ctx: PEVP_CIPHER_CTX; data_out: PByte; var outl: integer): integer; cdecl;
procedure EVP_DigestInit(ctx: PEVP_MD_CTX; md: PEVP_MD); cdecl;
function EVP_DigestInit_ex(ctx: PEVP_MD_CTX; md: PEVP_MD; impl: PENGINE): integer; cdecl;
function EVP_DigestUpdate(ctx: PEVP_MD_CTX; data: PByte; cnt: integer): integer; cdecl;
function EVP_DigestFinal(ctx: PEVP_MD_CTX; md: PByte; var s: cardinal): integer; cdecl;
function EVP_DigestFinal_ex(ctx: PEVP_MD_CTX; md: PByte; var s: cardinal): integer; cdecl;
procedure EVP_SignInit(ctx: PEVP_MD_CTX; md: PEVP_MD);
function EVP_SignInit_ex(ctx: PEVP_MD_CTX; md: PEVP_MD; impl: PENGINE): integer;
function EVP_SignUpdate(ctx: PEVP_MD_CTX; data: PByte; cnt: integer): integer;
function EVP_SignFinal(ctx: PEVP_MD_CTX; sig: PByte; var s: integer; pkey: PEVP_PKEY): integer; cdecl;
procedure EVP_VerifyInit(ctx: PEVP_MD_CTX; md: PEVP_MD);
function EVP_VerifyInit_ex(ctx: PEVP_MD_CTX; md: PEVP_MD; impl: PENGINE): integer;
function EVP_VerifyUpdate(ctx: PEVP_MD_CTX; data: PByte; cnt: integer): integer;
function EVP_VerifyFinal(ctx: PEVP_MD_CTX; sig: PByte; s: integer; pkey: PEVP_PKEY): integer; cdecl;
function X509_get_pubkey(cert: PX509): PEVP_PKEY; cdecl;
procedure BIO_free_all(a: PBIO); cdecl;
function PEM_write_bio_RSA_PUBKEY(bp: PBIO; x: PRSA): integer; cdecl;
function PEM_read_bio_PUBKEY(bp: PBIO; x: PPEVP_PKEY; cb: TSSLPasswordCallbackFunction; u: pointer): PEVP_PKEY; cdecl;
function PEM_write_bio_PUBKEY(bp: PBIO; x: PEVP_PKEY): integer; cdecl;
function RAND_load_file(const filename: PAnsiChar; max_bytes: longint): integer; cdecl;
function RAND_bytes(buf: PByte; num: integer): integer; cdecl;
function RAND_pseudo_bytes(buf: PByte; num: integer): integer; cdecl;
function RSA_generate_key(num: integer; e: Cardinal; cb: TSSLProgressCallbackFunction; cb_arg: pointer): PRSA; cdecl;
procedure RSA_free(r: PRSA); cdecl;
implementation
resourcestring
sLibeay32NotLoaded = 'libeay32.dll not loaded';
sAddAllAlgorithmsProcNotFound = 'OpenSSL_add_all_algorithms procedure not defined in libeay32.dll';
function EVP_aes_256_cbc: PEVP_CIPHER; cdecl external LIBEAY_DLL_NAME;
function EVP_md5; cdecl external LIBEAY_DLL_NAME;
function EVP_sha1; cdecl external LIBEAY_DLL_NAME;
function EVP_sha256; cdecl external LIBEAY_DLL_NAME;
function EVP_PKEY_assign; cdecl external LIBEAY_DLL_NAME;
function EVP_PKEY_new; cdecl external LIBEAY_DLL_NAME;
procedure EVP_PKEY_free; cdecl external LIBEAY_DLL_NAME;
function EVP_PKEY_assign_RSA(pkey: PEVP_PKEY; key: PRSA): integer;
begin
// Implemented as a macro in evp.h
result := EVP_PKEY_assign(pkey, EVP_PKEY_RSA, PAnsiChar(key));
end;
function EVP_PKEY_size; cdecl external LIBEAY_DLL_NAME;
procedure EVP_CIPHER_CTX_init; cdecl external LIBEAY_DLL_NAME;
procedure EVP_CipherInit_ex; cdecl external LIBEAY_DLL_NAME;
function EVP_CIPHER_CTX_cleanup; cdecl external LIBEAY_DLL_NAME;
function EVP_CIPHER_CTX_block_size; cdecl external LIBEAY_DLL_NAME;
function EVP_BytesToKey; cdecl external LIBEAY_DLL_NAME;
function EVP_EncryptInit_ex; cdecl external LIBEAY_DLL_NAME;
function EVP_EncryptInit; cdecl external LIBEAY_DLL_NAME;
function EVP_EncryptUpdate; cdecl external LIBEAY_DLL_NAME;
function EVP_EncryptFinal; cdecl external LIBEAY_DLL_NAME;
function EVP_DecryptInit_ex; cdecl external LIBEAY_DLL_NAME;
function EVP_DecryptInit; cdecl external LIBEAY_DLL_NAME;
function EVP_DecryptUpdate; cdecl external LIBEAY_DLL_NAME;
function EVP_DecryptFinal; cdecl external LIBEAY_DLL_NAME;
function EVP_DecryptFinal_ex; cdecl external LIBEAY_DLL_NAME;
function EVP_SealInit; cdecl external LIBEAY_DLL_NAME;
function EVP_SealUpdate(ctx: PEVP_CIPHER_CTX; data_out: PByte; var outl: integer; data_in: PByte; inl: integer): integer;
begin
// EVP_SealUpdate is #defined to EVP_EncryptUpdate in evp.h
result := EVP_EncryptUpdate(ctx, data_out, outl, data_in, inl);
end;
function EVP_SealFinal; cdecl external LIBEAY_DLL_NAME;
function EVP_OpenInit; cdecl external LIBEAY_DLL_NAME;
function EVP_OpenUpdate(ctx: PEVP_CIPHER_CTX; data_out: PByte; var outl: integer; data_in: PByte; inl: integer): integer;
begin
// EVP_OpenUpdate is #defined to EVP_DecryptUpdate in evp.h
result := EVP_DecryptUpdate(ctx, data_out, outl, data_in, inl);
end;
function EVP_OpenFinal; cdecl external LIBEAY_DLL_NAME;
procedure EVP_MD_CTX_init; cdecl external LIBEAY_DLL_NAME;
function EVP_MD_CTX_cleanup; cdecl external LIBEAY_DLL_NAME;
procedure EVP_DigestInit; external LIBEAY_DLL_NAME;
function EVP_DigestInit_ex; external LIBEAY_DLL_NAME;
function EVP_DigestUpdate; external LIBEAY_DLL_NAME;
function EVP_DigestFinal; external LIBEAY_DLL_NAME;
function EVP_DigestFinal_ex; external LIBEAY_DLL_NAME;
procedure EVP_SignInit(ctx: PEVP_MD_CTX; md: PEVP_MD);
begin
// Defined as a macro in evp.h
EVP_DigestInit(ctx, md);
end;
function EVP_SignInit_ex(ctx: PEVP_MD_CTX; md: PEVP_MD; impl: PENGINE): integer;
begin
// Defined as a macro in evp.h
result := EVP_DigestInit_ex(ctx, md, impl);
end;
function EVP_SignUpdate(ctx: PEVP_MD_CTX; data: PByte; cnt: integer): integer;
begin
// Defined as a macro in evp.h
result := EVP_DigestUpdate(ctx, data, cnt);
end;
function EVP_SignFinal; cdecl external LIBEAY_DLL_NAME;
procedure EVP_VerifyInit(ctx: PEVP_MD_CTX; md: PEVP_MD);
begin
// Defined as a macro in evp.h
EVP_DigestInit(ctx, md);
end;
function EVP_VerifyInit_ex(ctx: PEVP_MD_CTX; md: PEVP_MD; impl: PENGINE): integer;
begin
// Defined as a macro in evp.h
result := EVP_DigestInit_ex(ctx, md, impl);
end;
function EVP_VerifyUpdate(ctx: PEVP_MD_CTX; data: PByte; cnt: integer): integer;
begin
// Defined as a macro in evp.h
result := EVP_DigestUpdate(ctx, data, cnt);
end;
function EVP_VerifyFinal; cdecl external LIBEAY_DLL_NAME;
function X509_get_pubkey; cdecl; external LIBEAY_DLL_NAME;
procedure BIO_free_all; cdecl external LIBEAY_DLL_NAME;
function PEM_write_bio_RSA_PUBKEY; cdecl external LIBEAY_DLL_NAME;
function PEM_read_bio_PUBKEY; cdecl external LIBEAY_DLL_NAME;
function PEM_write_bio_PUBKEY; cdecl external LIBEAY_DLL_NAME;
function RAND_load_file; cdecl external LIBEAY_DLL_NAME;
function RAND_bytes; cdecl external LIBEAY_DLL_NAME;
function RAND_pseudo_bytes; cdecl external LIBEAY_DLL_NAME;
function RSA_generate_key; cdecl external LIBEAY_DLL_NAME;
procedure RSA_free; cdecl external LIBEAY_DLL_NAME;
end.
来源:https://stackoverflow.com/questions/31595241/tplb-3-openssl-decrypt-aes-256-cbc-encrypted-with-ruby-2-0-0-opensslcipher