📜 ⬆️ ⬇️

RSA encryption through the OpenSSL library in Delphi



On duty, the developers met the task of encrypting text strings with the RSA algorithm using public and private keys in PEM format. When studying this issue, the choice fell on the use of the OpenSSL library. I want to share examples of the implementation of encryption functionality in Delphi. Actions were performed in Windows, Borland Delphi 7 development environment.

Where to begin?


First of all, the openssl package is needed, or rather the libeay32.dll library. And to connect the library, we need a ready-made unit of libeay32.pas , which needs to be connected to the project. In my example, there is no functionality for generating keys from a project, how to do this can be read in the material at the link below the article. To generate a key pair, I used openssl myself with the following commands from cmd:
We generate a private key and retrieve it from it with a public key.
openssl genrsa 1024 > private.pem openssl rsa -in private.pem -pubout > public.pem 

Where 1024 is the key bit. I note that the length of the string for encryption also depends on the bit depth of the key. If the key is 1024 bits, then we can encrypt a total of 128 bytes, the same size that is equal to the size of the key. Below I show the function that determines the size of the RSA key structure. But that's not all. This buffer will decrease by 11 bytes if during encryption you specify the padding parameter responsible for data alignment - PKCS # 1 .

Having generated the key in 2048 bits, we can encrypt 256 bytes. This increases the size of the output encrypted text, even if only 1 byte is encrypted.
')
For my task, 117 bytes were enough to fit the card data, the size of the rows in the database, of course, increased significantly. But that requires security.

Main functions


The main thing that we will use for encryption is:

Initialization of crypto functions

 OpenSSL_add_all_algorithms; OpenSSL_add_all_ciphers; OpenSSL_add_all_digests; ERR_load_crypto_strings; ERR_load_RSA_strings; 

Destroy

 EVP_cleanup; ERR_free_strings; 

Key reading

 //     PEM,   RSA // bp  ,   RSA   x, cb –      . function PEM_read_bio_PrivateKey(bp: pBIO; var x: pEVP_PKEY; cb: TPWCallbackFunction; u: pointer): pEVP_PKEY; cdecl; //     PEM,   RSA function PEM_read_bio_PUBKEY(bp: pBIO; var x: pEVP_PKEY; cb: TPWCallbackFunction; u: pointer): pEVP_PKEY; cdecl; 

And encryption / decryption functions

 /// flen   from   _to   RSA       // padding.   /   -1   //   function RSA_public_encrypt(flen: integer; from: PCharacter; _to: PCharacter; rsa: pRSA; padding: integer): integer; cdecl; //   function RSA_private_encrypt(flen: integer; from: PCharacter; _to: PCharacter; rsa: pRSA; padding: integer): integer; cdecl; //   function RSA_public_decrypt(flen: integer; from: PCharacter; _to: PCharacter; rsa: pRSA; padding: integer): integer; cdecl; //   function RSA_private_decrypt(flen: integer; from: PCharacter; _to: PCharacter; rsa: pRSA; padding: integer): integer; cdecl; 

Let's start the implementation


So, we have the generated keys, the dll is in the project folder, liblea32.pas is connected in uses. Call openssl initialization procedures:

 procedure LoadSSL; begin OpenSSL_add_all_algorithms; OpenSSL_add_all_ciphers; OpenSSL_add_all_digests; ERR_load_crypto_strings; ERR_load_RSA_strings; end; procedure FreeSSL; begin EVP_cleanup; ERR_free_strings; end; 

We write functions of loading of keys.

KeyFile - up to the key ('C: \ key.pem'):

 function LoadPublicKey(KeyFile: string) :pEVP_PKEY ; var mem: pBIO; k: pEVP_PKEY; begin k:=nil; mem := BIO_new(BIO_s_file()); //BIO   BIO_read_filename(mem, PAnsiChar(KeyFile)); //     BIO try result := PEM_read_bio_PUBKEY(mem, k, nil, nil); // BIO   pEVP_PKEY,    nil,        finally BIO_free_all(mem); end; end; function LoadPrivateKey(KeyFile: string) :pEVP_PKEY; var mem: pBIO; k: pEVP_PKEY; begin k := nil; mem := BIO_new(BIO_s_file()); BIO_read_filename(mem, PAnsiChar(KeyFile)); try result := PEM_read_bio_PrivateKey(mem, k, nil, nil); finally BIO_free_all(mem); end; end; 

Calling key reading and error handling

 var FPublicKey: pEVP_PKEY; FPrivateKey: pEVP_PKEY; err: Cardinal; … FPublicKey := LoadPublicKey('C:\public.key'); FPrivateKey := LoadPrivateKey('C:\private.key'); //if FPrivateKey = nil then //    ,    if FPublicKey = nil then begin err := ERR_get_error; repeat log.Lines.Add(string(ERR_error_string(err, nil))); err := ERR_get_error; until err = 0; end; 

Encryption (Public Key)

 var rsa: pRSA; //  RSA size: Integer; FCryptedBuffer: pointer; //   b64, mem: pBIO; str, data: AnsiString; len, b64len: Integer; penc64: PAnsiChar; size: Integer; err: Cardinal begin rsa := EVP_PKEY_get1_RSA(FPrivateKey); //  RSA  EVP_PKEY_free(FPrivateKey); //  pEVP_PKEY size := RSA_size(rsa); //    GetMem(FCryptedBuffer, size); //     str := AnsiString('Some text to encrypt'); //    // len := RSA_public_encrypt(Length(str), //     PAnsiChar(str), //   FCryptedBuffer, //   rsa, //   RSA_PKCS1_PADDING //   ); if len > 0 then //     begin //       base64 b64 := BIO_new(BIO_f_base64); // BIO  base64 mem := BIO_push(b64, BIO_new(BIO_s_mem)); // Stream try BIO_write(mem, FCryptedBuffer, len); //   Stream    BIO_flush(mem); b64len := BIO_get_mem_data(mem, penc64); //    base64 SetLength(data, b64len); //     Move(penc64^, PAnsiChar(data)^, b64len); //    data   base64 finally BIO_free_all(mem); end; end else begin //  ,     -1 err := ERR_get_error; repeat log.Lines.Add(string(ERR_error_string(err, nil))); err := ERR_get_error; until err = 0; end; RSA_free(rsa); end; 

Decryption (secret key)

 var rsa: pRSA; out_: AnsiString; str, data: PAnsiChar; len, b64len: Integer; penc64: PAnsiChar; b64, mem, bio_out, bio: pBIO; size: Integer; err: Cardinal; begin //ACryptedData : string; //   base64 rsa := EVP_PKEY_get1_RSA(FPublicKey); size := RSA_size(rsa); GetMem(data, size); //       GetMem(str, size); //        base64 //Decode base64 b64 := BIO_new(BIO_f_base64); mem := BIO_new_mem_buf(PAnsiChar(ACryptedData), Length(ACryptedData)); BIO_flush(mem); mem := BIO_push(b64, mem); BIO_read(mem, str , Length(ACryptedData)); //       BIO_free_all(mem); //  len := RSA_private_decrypt(size, PAnsiChar(str), data, rsa, RSA_PKCS1_PADDING); if len > 0 then begin //   data    «»  , ,    out_         data SetLength(out_, len); Move(data^, PAnsiChar(out_ )^, len); end else begin //  ,     -1 err := ERR_get_error; repeat log.Lines.Add(string(ERR_error_string(err, nil))); err := ERR_get_error; until err = 0; end; end; 

And the conclusion is an example of reading the key "wired" in the application

In the example, the private key and its reading are specified, it is “sewn up” with the same success and the public key is read by the function PEM_read_bio_PUBKEY
 var mem, keybio: pBIO; k: pEVP_PKEY; keystring: AnsiString; begin keystring := '-----BEGIN RSA PRIVATE KEY-----' + #10 + 'MIICXgIBAAKBgQCfydli2u2kJfb2WetkOekjzQIg7bIuU7AzAlBUPuA72UYXWnQ/' + #10 + 'XcdSzEEMWSBLP7FO1vyVXR4Eb0/WqthF0ZViOK5bCN9CnR/1GMMiSqmIdByv/gUe' + #10 + 'Z/UjGrKmxeQOoa2Yt0MJC64cNXgnKmYC7ui3A12LlvNdBBEF3WpcDbv+PQIDAQAB' + #10 + 'AoGBAJnxukKHchSHjxthHmv9byRSyw42c0g20LcUL5g6y4Zdmi29s+moy/R1XOYs' + #10 + 'p/RXdNfkQI0WnWjgZScIij0Z4rSs39uh7eQ5qxK+NH3QIWeR2ZNIno9jAXPn2bkQ' + #10 + 'odS8FPzbZM9wHhpRvKW4FNPXqTc3ZkTcxi4zOwOdlECf9G+BAkEAzsJHgW1Isyac' + #10 + 'I61MDu2qjMUwOdOBYS8GwEBfi/vbn/duwZIBXG/BZ7Pn+cBwImfksEXwx0MTkgF3' + #10 + 'gyaChUSu+QJBAMXX3d94TwcF7lG9zkzc+AR/Onl4Z5UAb1GmUV57oYIFVgW1RIOk' + #10 + 'vqynXWrTjTOg9C9j+VEpBG67LcnkwU16JmUCQH7pukKz9kAhnw43PcycDmhCUgvs' + #10 + 'zCn/V8GCwiOHAZT7qLyhBrzazHj/cZFYknxMEZAyHk3x2n1w8Q9MACoVsuECQQDF' + #10 + 'U7cyara31IyM7vlS5JpjMdrKyPLXRKXDFFXYHQtLubLA4rlBbBHZ9txP7kzJj+G9' + #10 + 'WsOS1YxcPUlAM28xrYGZAkEArVKJHX4dF8UUtfvyv78muXJZNXTwmaaFy02xjtR5' + #10 + 'uXWT1QjVN2a6jv6AW7ukXiSoE/spgfvdoriMk2JSs88nUw==' + #10 + '-----END RSA PRIVATE KEY-----' ; k := nil; keybio := BIO_new_mem_buf(Pchar(keystring), -1); mem := BIO_new(BIO_s_mem()); BIO_read(mem, PAnsiChar(keystring), length(PAnsiChar(keystring))); try result := PEM_read_bio_PrivateKey(keybio, k, nil, nil); finally BIO_free_all(mem); end; end; 

This is where my implementation examples end. Project sources are available on Github .

Sources:



UPD
From the discussions in the comments I will make additions: if we encrypt the string we need with a public key, then it is decrypted only with a secret key and vice versa - if it is secret, then it can be decrypted only with a public key. In my case, the client has a public key with which he encrypts the data and only on the server can they be decrypted with a private key.

Source: https://habr.com/ru/post/280302/


All Articles