Sharing AES Key using RSA with OpenSSL
By: Gustavo Simões
Tags: Rsa, Cryptography, Open Source, Openssl

If you work with modern backend development it is inevitable that one day you’ll integrate your application with external services. When the exchanged data includes sensible information (ex: users personal information, company internal report) it is important to encrypt it to provide an additional layer of protection in the event of a leak or cyber attack.

Symmetric cryptography

For many years, cryptography was achieved using a shared key between two parties (sender and receiver). The key is used to encrypt a message by one party and to decrypt it by the other, hence the name symmetric. Symmetric cryptography remains widely used nowadays but it has some downsides.

Symmetric cryptography downside

To establish an encrypted communication using symmetric cryptography one party must generate the key and send to the other. In earlier implementations, this was achieved on a personal meeting. In the world of online services, this is not viable option as the physical distance between each party may be big and the implementation must be quick.

Sharing the key online introduces some security risks as if discovered by someone capable of intercepting the messages (ex: staff with network access), this person can view the original content, change it and even pass as the original sender without any of the parties realizing it (man-in-the-middle attack).

Asymmetric cryptography

In asymmetric cryptography you have a public / private key pair where one is used to encrypt a message and the other to decrypt it. Currently, the most popular asymmetric cryptography algorithm is RSA (Rivest-Shamir-Adleman) which includes the following principles:

The private key is private; The public key can be shared; The public key derives from the private key; You cannot guess the private key from the public key; A message encrypted with the public key can only be decrypted with the corresponding private key; A message encrypted with the private key can only be decrypted with the corresponding public key.

RSA implementation

Let’s say Alice wants to send a encrypted message to Bob 😅. Using RSA cryptography, each party generates a key pair and share their public key with the other. Alice encrypts her message with Bob’s public key and then with her private key before finally sending it to Bob. Bob receives the encrypted message, decrypts it with Alice’s public key and then with his private key to get to the original message.

Why encrypt with private key?

A very common doubt about this process is to understand why Alice would want to encrypt the message with her private key as anyone with her public key can decrypt it. The fact that the message can be decrypted with Alice’s public key assures that it was encrypted with her private key (RSA principle #6). This way, assuming that only Alice knows her private key, Bob can verify that the message was sent by Alice. This process is known as signing the message.

Why not just use RSA encryption

RSA was not designed to process large amounts of data. In this scenario, a symmetric algorithm like AES (Advanced Encryption Standard) is recommended.

Here is a comparison of RSA and AES algorithms used to encrypt and decrypt a 100.000 lines csv file with name, email and age columns using Go. Code available in this repository.

RSA

Encrypt: 30.31 s

Decrypt: 569.62 s

AES

Encrypt: 1.36 s

Decrypt: 1.20 s

Cryptography was applied to the text values, not the files directly. Bear in mind that those values may vary depending on the implementation you use. In this case, it was used RSA with OAEP (Optimal asymmetric encryption padding) and SHA256 hashing, and AES with GCM (Galois/Counter Mode).

AES + RSA

A common way to solve this issue is to encrypt / decrypt the desired content with AES and share the AES key using RSA. This way you can take advantage of AES efficiency while minimizing its downside (sending the key).

AES + RSA implementation with OpenSSL

OpenSSL is a robust, commercial-grade, full-featured toolkit for TLS (Transport Layer Security) and SSL (Secure Sockets Layer) protocols and also a general-purpose cryptography library. OpenSSL was chosen to be used in this article as it is a widely used tool, available in all platforms for many years.

Bellow are the steps for a implementation of AES and RSA to establish a encrypted communication between Alice and Bob using OpenSSL in UNIX systems.

1. Generate RSA key pairs

Alice generates a private key file private.pem.

openssl genrsa -out private.pem

The recommended size for RSA keys today, considering computational power for brute force attacks, is 2048 bits, which is the default in this command.

Alice extract the public key from the private key (RSA principle #3) to another file public.pem.

openssl rsa -in private.pem -pubout -out public.pem

2. Exchange public keys

Alice and Bob share their public pem file with each other.

A 2048 bit RSA key may be reused for a long time but it is recommended to be replaced at least once a year depending on the interest of third parties to crack it.

3. Generate AES key

In this example, the AES algorithm is used with 256 bits CBC (Cipher-block chaining) mode of operation. This mode requires a 32 bytes key and a 16 bytes initiation vector (iv). Alice generates a key and iv in hex format and store in variables for subsequent use.

AES_KEY=$(openssl rand -hex 32)

AES_IV=$(openssl rand -hex 16)

Ideally you should generate a new key / iv pair on each use, but you can reuse the key for some time and change only the iv which does not need to be encrypted. OpenSSL encryption function does not support GCM mode of operation.

4. Encrypt data with AES key

Alice encrypts a text file data.csv that she wants to send to Bob.

openssl enc -aes-256-cbc -K $AES_KEY -iv $AES_IV -in data.csv -out data.csv.enc

5. Encrypt AES key with RSA

Alice encrypts the AES key using Bob’s public key to a file aes_key.enc.

echo $AES_KEY | openssl rsautl -encrypt -pubin -inkey public.pem - oaep -out aes_key.enc

The argument -oaep specifies the use of OAEP padding which is recommended.

6. Sign message

Alice creates a signature file aes_key.enc.sig from aes_key.enc using her private key.

openssl dgst -sha256 -sign private.pem -out aes_key.enc.sig aes_key.enc

7. Send encrypted data, encrypted AES key, signature and iv

Alice sends the AES encrypted data file data.csv.env, the AES encrypted key aes_key.enc, the signature file aes_key.enc.sig and the plain text iv to Bob.

8. Verify signature

Bob verifies if the received data came from Alice by checking if aes_key.enc file hash matches the signature file decrypted with Alice’s public key.

openssl dgst -sha256 -verify public.pem -signature aes_key.enc.sig aes_key.enc

The argument -sha256 specify the hashing algorithm used to encrypt the file.

9. Decrypt AES key

Bob decrypts aes_key.enc file using his private key to get the AES key generated by Alice.

AES_KEY=$(openssl rsautl -decrypt -inkey private.pem -in aes_key.enc)

10. Decrypt data

Bob decrypts data.csv.enc file and get the original file content.

openssl enc -d -aes-256-cbc -K $AES_KEY -iv $AES_IV -in data.csv.enc -out data.csv

Conclusion

AES is a efficient algorithm for large files cryptography but sharing the key is a security risk. One way to minimize this risk is to use RSA algorithm to send the key. OpenSSL provides a easy cryptography implementation for this logic that can be automated.