OpenSSL in Delphi 7 [closed]

0

I need to use the OpenSSL functions directly in Delphi 7 and I need help because I can not do it.

    
asked by anonymous 29.10.2014 / 20:45

1 answer

1

Translation of this answer from shunty

  

Here are some routines taken directly from some fonts I've worked on. They use most of the encryption methods you mentioned. As mentioned in the comment, you will need to stay close to the OpenSSL documentation. I created all the code below by reading through the sources in C program openssl.exe. (Downloaded from openssl.org)

     

It's not perfect and makes some presumptions, but it does all the basics for the routines used in delphi.

     

The original inspiration, as already mentioned several times in the SO, has been taken from the sources at link

     

An imported unit was added at the end to supplement what I had in the Indy headers when I created these methods. I have not seen it recently, but some of these methods should already be available on 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;

Unit with Indy methods

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.

PS: Also, this site has great examples for using libeay32.dll link

    
29.10.2014 / 20:59