Delphi / Pascal Example for Calling OpenSSL EVP functions

匿名 (未验证) 提交于 2019-12-03 01:47:02

问题:

Does anyone have a Delphi / Pascal example for calling the below OpenSSL functions...

http://svn.freepascal.org/cgi-bin/viewvc.cgi/trunk/packages/openssl/src/openssl.pas?revision=17634&view=markup

I am specifically interested in:

procedure EVP_CIPHER_CTX_init(a: PEVP_CIPHER_CTX);  function EVP_CIPHER_CTX_cleanup(a: PEVP_CIPHER_CTX): cint;  function EVP_CIPHER_CTX_set_key_length(x: PEVP_CIPHER_CTX; keylen: cint): cint;  function EVP_CIPHER_CTX_ctrl(ctx: PEVP_CIPHER_CTX; type_, arg: cint; ptr: Pointer): cint; // function EVP_EncryptInit(ctx: PEVP_CIPHER_CTX; const chipher_: PEVP_CIPHER;const key, iv: PByte): cint;  function EVP_EncryptUpdate(ctx: PEVP_CIPHER_CTX; out_: pcuchar;outlen: pcint; const in_: pcuchar; inlen: cint): cint;  function EVP_EncryptFinal(ctx: PEVP_CIPHER_CTX; out_data: PByte; outlen: pcint): cint;  function EVP_DecryptInit(ctx: PEVP_CIPHER_CTX; chiphir_type: PEVP_CIPHER;const key, iv: PByte): cint;  function EVP_DecryptUpdate(ctx: PEVP_CIPHER_CTX; out_data: PByte;outl: pcint; const in_: PByte; inl: cint): cint;  function EVP_DecryptFinal(ctx: PEVP_CIPHER_CTX; outm: PByte; outlen: pcint): cint;

Thank you!

p.s. I have seen http://www.disi.unige.it/person/FerranteM/delphiopenssl/

Unfortunately that library does not include those functions.

回答1:

Here are a couple of routines taken straight out of some code I once worked on. They use most of the encryption methods you mention. As mentioned in the comments you really need to get intimate with the OpenSSL docs. I created the stuff below by reading them and going through the C source for the openssl.exe program. (Downloaded from openssl.org).

It's not perfect and makes a couple of assumptions but it does show the basics of using the routines in Delphi.

The original inspiration, as I have mentioned several times before on SO(!), was taken from the stuff at http://www.disi.unige.it/person/FerranteM/delphiopenssl/ that you've already linked to above.

EDIT: Added an import unit at the bottom to supplement what was in the Indy headers when I created these methods. I haven't looked recently so some of this may already be available in Indy.

function EVP_Encrypt_AES256(Value: TBytes; APassword: TBytes): TBytes; var   cipher: PEVP_CIPHER;   ctx: EVP_CIPHER_CTX;   salt, key, iv, buf: TBytes;   block_size: integer;   buf_start, out_len: integer; begin   cipher := EVP_aes_256_cbc;   salt := EVP_GetSalt;   EVP_GetKeyIV(APassword, cipher, salt, key, iv);    EVP_CIPHER_CTX_init(@ctx);   try     EVP_EncryptInit(@ctx, cipher, @key[0], @iv[0]);     block_size := EVP_CIPHER_CTX_block_size(@ctx);     SetLength(buf, Length(Value) + block_size + SALT_MAGIC_LEN + PKCS5_SALT_LEN);     buf_start := 0;     Move(PAnsiChar(SALT_MAGIC)^, buf[buf_start], SALT_MAGIC_LEN);     Inc(buf_start, SALT_MAGIC_LEN);     Move(salt[0], buf[buf_start], PKCS5_SALT_LEN);     Inc(buf_start, PKCS5_SALT_LEN);     EVP_EncryptUpdate(@ctx, @buf[buf_start], @out_len, @Value[0], Length(Value));     Inc(buf_start, out_len);     EVP_EncryptFinal(@ctx, @buf[buf_start], @out_len);     Inc(buf_start, out_len);     SetLength(buf, buf_start);     result := buf;   finally     EVP_CIPHER_CTX_cleanup(@ctx);   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;

Decryption:

function EVP_Decrypt_AES256(const Value: TBytes; APassword: TBytes): TBytes; var   cipher: PEVP_CIPHER;   ctx: EVP_CIPHER_CTX;   salt, key, iv, buf: TBytes;   src_start, buf_start, out_len: 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);     src_start := 0;   end;    EVP_CIPHER_CTX_init(@ctx);   try     EVP_DecryptInit(@ctx, cipher, @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);     Inc(buf_start, out_len);     EVP_DecryptFinal(@ctx, @buf[buf_start], @out_len);     Inc(buf_start, out_len);     SetLength(buf, buf_start);     result := buf;   finally     EVP_CIPHER_CTX_cleanup(@ctx);   end; end;

My extra import unit:

unit libeay32;  {   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 = '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; 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_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; 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_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.


回答2:

For me the open ssl which is mentioned works however it is not compatible with OpenSSL. A compatible function is needed when you want to decrypt it in java/javascript. To make it work you need to replace the EVP_GetKeyIV routine with the code underneath.

procedure EVP_GetKeyIV(APassword: TBytes; ACipher: PEVP_CIPHER; const ASalt: TBytes; out Key, IV: TBytes); begin   SetLength(Key,EVP_MAX_KEY_LENGTH);   SetLength(iv,EVP_MAX_IV_LENGTH);    EVP_BytesToKey(ACipher,EVP_md5, @ASalt[0] ,@APassword[0]  , Length(APassword),1, @Key[0], @IV[0]);  end;

To encrypt a value and base encode it I use the following routine:

function EVP_Encrypt_Base64_AES256(Value: RawByteString; APassword: AnsiString): String; var bytes : TBytes; begin   bytes:= EVP_Encrypt_AES256(BytesOf(Value),BytesOf(APassword));   Result:=EncodeBase64(pointer(Bytes), length(Bytes)); end;  sEncrypted:=EVP_Encrypt_Base64_AES256('Test','Password123');

To test it you can save the sEncrypted string into a file in.txt. (Do not forget to place a enter at the end) To decrypt using open ssl use:

Open sll command

openssl enc -d -aes-256-cbc   -k Password123  -a -in in.txt -out  out.txt

In the out.txt you will see the origninal value Test



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!