The symmetric encryption is the most used cryptographic operation to protect the confidentiality of the information whether it is personal information or corporate sensitive data.
The main reason why symmetric encryption is preferred in most of the cases instead of asymmetric encryption is not because is more secure but more performant. Therefore, for encrypting high amounts of data, the symmetric encryption is normally the chosen one for this purpose.
Although the asymmetric encryption is more secure, the symmetric encryption algorithms like AES still provide a great security level, and there are no signs that in the following years will be deemed as unsafe. That’s why AES is still considered a standard for protecting sensitive information from different governments around the world.
The AES encryption algorithm
Nowadays, AES standard supports 3 different key lengths that are all of them considered as safe. The key bit lengths are 128, 192 and 256 and, as you may suppose, the longer the key the more secure it is but, the more secure the lower the performance is. So you need to balance performance and security level depending on the encryption requirements of the project you are facing.
Currently, symmetric keys lower than 128 bits are not safe enough, and that’s why algorithms like 3DES (112 bits of key length) are not considered anymore for this kind of encryption.
However, bear in mind that 128 bits of key length would be the next one to be deemed as obsolete to provide enough security level and this would entail the migration of such encryption to a safer one with an applicable operational costs.
Encrypt and decrypt a file with AES using openssl
There are several ways to achieve this goal like using any python module to do this operation or with GnuPG but, in this post, the main tool to perform this operations will be openssl.
Let’s begin to get the AES encryption algorithms that it is supported by openssl:
$ openssl list-cipher-commands | grep -i aes
aes-128-cbc
aes-128-ecb
aes-192-cbc
aes-192-ecb
aes-256-cbc
aes-256-ecb
For the following example, we will use “aes-256-cbc” encryption algorithm to encrypt a plain text file:
$ openssl enc -aes-256-cbc -pass pass:thisisatest1 -in plain.txt -out encrypted
$ file plain.txt
plain.txt: ASCII text
$ file encrypted
encrypted: data
Then let’s decrypt the recent encrypted file:
$ openssl enc -aes-256-cbc -pass pass:thisisatest1 -d -in encrypted -out decrypted
$ cat plain.txt
hello world
$ cat decrypted
hello world
That’s it, simple as that. No key management and with one single command you can encrypt or decrypt a file with the protection of a password.
Nevertheless, you may wonder where the key went since AES, as other encryption algorithms, relies on keys to perform the cryptographic operations. This question and other security aspects will be covered in the next section of the post.
Improve the security posture of openssl encryption by passing the password through alternate ways
In the previous section, we showed the required command lines to encrypt or decrypt a file but there are certain aspects that we can improve to increase the overall security of the process like avoiding of passing the password in parameters. The issue with passing the password in the command line is that it could be logged in files or command history.
Therefore, to get rid of the password in the command line when encrypting or decrypting with openssl, you just need to omit the option “-pass” so openssl will ask you to introduce it in a prompt:
$ openssl enc -aes-256-cbc -in plain.txt -out encrypted
enter aes-256-cbc encryption password:
Verifying - enter aes-256-cbc encryption password:
$ file plain.txt
plain.txt: ASCII text
$ file encrypted
encrypted: data
$ openssl enc -aes-256-cbc -d -in encrypted -out decrypted
enter aes-256-cbc decryption password:
$ file decrypted
decrypted: ASCII text
$ cat plain.txt
Hello World
$ cat decrypted
Hello World
If you introduce a wrong password, then the result won’t be the plain text file:
$ openssl enc -aes-256-cbc -d -in encrypted -out decrypted2
enter aes-256-cbc decryption password:
bad decrypt
139645204400016:error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt:evp_enc.c:618:
$ file decrypted2
decrypted2: empty
By doing this encryption or decryption without passing the password in the command line, we are not revealing the password in history or logs reducing the risk of leaking sensitive information:
$ history | tail
12 openssl enc -aes-256-cbc -in plain.txt -out encrypted
13 file plain.txt
14 file encrypted
15 openssl enc -aes-256-cbc -d -in encrypted -out decrypted
16 file decrypted
17 cat plain.txt
18 cat decrypted
19 openssl enc -aes-256-cbc -d -in encrypted -out decrypted2
20 file decrypted2
21 history | tail
Despite that this is the most secure way to introduce a password to perform such cryptographic operations, it is not automation friendly since it will pause the execution due to the password prompt. In order to overcome this issue and keep the password as safe as possible, we can use an environment variable set with the password value by using the read command and reuse it for the next openssl encryptions:
$ echo "environment variable test" > plain2.txt
$ read SECRET
topsecret1
$ ls
plain2.txt
$ openssl enc -aes-256-cbc -in plain2.txt -out encrypted2 -pass pass:$SECRET
$ ls
encrypted2 plain2.txt
$ file encrypted2
encrypted2: data
$ openssl enc -aes-256-cbc -d -in encrypted2 -out decrypted2 -pass pass:$SECRET
$ ls
decrypted2 encrypted2 plain2.txt
$ cat decrypted2
environment variable test
Checking the commands history, no clear text passwords are shown by using this method:
$ history | tail
46 echo "environment variable test" > plain2.txt
47 read SECRET
48 ls
49 openssl enc -aes-256-cbc -in plain2.txt -out encrypted2 -pass pass:$SECRET
50 ls
51 file encrypted2
52 openssl enc -aes-256-cbc -d -in encrypted2 -out decrypted2 -pass pass:$SECRET
53 ls
54 cat decrypted2
55 history | tail
Another way to avoid prompts, but keeping the password as safe as possible is by creating a file with the password and protect it with the correct permissions set (chmod 600 or chmod g-rwx,o-rwx), and hide it (doted file name):
$ echo "using a password file" > plain3.txt
$ cat > .passwd
topsecret2
$ chmod 600 .passwd
$ ls
plain3.txt
$ openssl enc -aes-256-cbc -in plain3.txt -out encrypted3 -pass file:.passwd
$ ls
encrypted3 plain3.txt
$ file encrypted3
encrypted3: data
$ openssl enc -aes-256-cbc -d -in encrypted3 -out decrypted3 -pass file:.passwd
$ ls
decrypted3 encrypted3 plain3.txt
$ cat decrypted3
using a password file
$ cat > wrong_passwd
wrong1
$ ls
decrypted3 encrypted3 plain3.txt wrong_passwd
$ openssl enc -aes-256-cbc -d -in encrypted3 -out wrong_decrypted -pass file:wrong_passwd
bad decrypt
140314079197072:error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt:evp_enc.c:618:
$ ls
decrypted3 encrypted3 plain3.txt wrong_decrypted wrong_passwd
$ file wrong_decrypted
wrong_decrypted: data
After the previous sequence of commands, if we inspect the history we will see no passwords again:
$ history | tail
84 file encrypted3
85 openssl enc -aes-256-cbc -d -in encrypted3 -out decrypted3 -pass file:.passwd
86 ls
87 cat decrypted3
88 cat > wrong_passwd
89 ls
90 openssl enc -aes-256-cbc -d -in encrypted3 -out wrong_decrypted -pass file:wrong_passwd
91 ls
92 file wrong_decrypted
93 history | tail
Use PBDKF2 method to improve security against brute force attacks
Openssl versions previous 1.1.1 provided a weak key derivation from passwords that attackers could break by using brute force. This means that an attacker with enough computation power could discover in short time the key used to encrypt the protected file, hence, decrypt it and finally steal the information.
In order to reduce the risk of dictionary or brute force attacks, we can use a more robust PBDKF (Password Based Derivation Key Function) method but, in order to leverage openssl in this terms, it needs to be on version 1.1.1 or above.
In the following example, the openssl is used to encrypt and decrypt with AES256, CBC mode, with PBKDF2, 310000 of iterations, SHA256 and salt:
$ echo "PBKDF plain text" > plain.txt
$ cat plain.txt
PBKDF plain text
$ openssl enc -aes-256-cbc -pbkdf2 -iter 310000 -md sha256 -salt -in plain.txt -out encrypted
enter aes-256-cbc encryption password:
Verifying - enter aes-256-cbc encryption password:
$ ls
encrypted plain.txt
$ file encrypted
encrypted: openssl enc'd data with salted password
$ openssl enc -aes-256-cbc -d -pbkdf2 -iter 310000 -md sha256 -salt -in encrypted -out decrypted
enter aes-256-cbc decryption password:
$ ls
decrypted encrypted plain.txt
$ cat decrypted
PBKDF plain text
The “-pbkdf2” enforce openssl to use this key derivation method instead of the default method which is weak and not recommendable. This key derivation can be configured with “-iter”, which sets the number of hashing operations that needs to be done on the password, the “-md” that sets the hashing algorithm used, and “-salt” to append on password a random value before being hashed.
The combination of these parameters will make the brute force being more harder since each of the password tested, needs to compute 310000 times the PBKDF2-HMAC-SHA256 operation. Note that this 310000 iterations or above are the recommended by OWASP best practices for the algorithm PBKDF2-HMAC-SHA256.
On the other hand, the decryption iterations needs to be the exact number as the used during encryption otherwise the decryption will fail:
$ openssl enc -aes-256-cbc -pbkdf2 -iter 310000 -md sha256 -salt -in plain.txt -out encrypted -pass pass:_T3sting1
$ cat plain.txt
PBKDF plain text
$ file encrypted
encrypted: openssl enc'd data with salted password
$ openssl enc -aes-256-cbc -d -pbkdf2 -iter 290000 -md sha256 -salt -in encrypted -out decrypted -pass pass:_T3sting1
bad decrypt
140395810730432:error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt:../crypto/evp/evp_enc.c:564:
$ file decrypted
decrypted: Non-ISO extended-ASCII text, with no line terminators
$ openssl enc -aes-256-cbc -d -pbkdf2 -iter 310000 -md sha256 -salt -in encrypted -out decrypted -pass pass:_T3sting1
$ cat decrypted
PBKDF plain text
This time, the password is passed through command line just for showing the not matching iteration issue when decrypting. The recommendation is still to not passing the password in clear text on the same command line.
Use a different encryption or cipher mode
In all of the examples showed previously, the AES256 encryption algorithm was used alongside with CBC (Cipher Block Chaining) mode. This encryption mode is vulnerable to oracle padding attacks and does not provide authentication unlike the GCM (Galois/Counter Mode). However, this doesn’t mean that GCM mode is the best for every scenario because it is, for example, vulnerable to cycling attacks or forgery attacks.
Nevertheless, in the next example, the openssl will encrypt with CTR (Counter) mode:
$ echo "encrypt with CTR mode" > plain.txt
$ openssl enc -aes-256-ctr -pbkdf2 -iter 310000 -md sha256 -salt -in plain.txt -out encrypted
enter aes-256-ctr encryption password:
Verifying - enter aes-256-ctr encryption password:
$ ls
encrypted plain.txt
$ cat plain.txt
encrypt with CTR mode
$ file encrypted
encrypted: openssl enc'd data with salted password
$ openssl enc -aes-256-ctr -d -pbkdf2 -iter 310000 -md sha256 -salt -in encrypted -out decrypted
enter aes-256-ctr decryption password:
$ ls
decrypted encrypted plain.txt
$ cat decrypted
encrypt with CTR mode
The CTR mode is not vulnerable to oracle padding attacks but it lacks authentication. Finally, the cipher mode ECB is not recommended because it doesn’t hide well the patterns. This means that similar texts encrypted with the same key will produce a similar cipher text.