Thursday, July 05, 2012

AES Encryption & Decryption from the command line with OpenSSL

Today, I wanted to gain a deeper understanding of AES encryption. I have succesfully used AES encryption with ipsec vpn's. But here I want to examine what are the inputs and outputs from the AES algorithm, and do some sanity checks with test data using openssl from the command-line.

On the wikipedia page for AES, they mention, "..Test vectors are a set of known ciphers for a given input and key. NIST distributes the reference of AES test vectors as AES Known Answer Test (KAT) Vectors (in ZIP format)."

That sounds like what I need as a reference, so I downloaded:

...and selected (guessed?) file "CBCVarKey128.rsp" as suitable, because I wanted to use a 128 bit key length,

Here is one example (from 128 possible examples) from that file:
KEY = 80000000000000000000000000000000
IV = 00000000000000000000000000000000
PLAINTEXT = 00000000000000000000000000000000
CIPHERTEXT = 0edd33d3c621e546455bd8ba1418bec8

Ok, first let's sanity check we can generate the required PLAINTEXT. I'm using xxd to do this, as I explained in an earlier blog post.

# echo -n '00000000000000000000000000000000' | xxd -p -r | hexdump -C
00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|

So the above looks good. We are generating the 16 bytes of test data, from a 32 character hexadecimal string.

Openssl allow you to specify the Key and the 'Initialization Vector' on the command like, as hexadecimal strings, using the '-K' and '-iv' parameters. Be careful, thats an uppercase K for the key. As another sanity check, it is useful to use the '-P' option, again uppercase, to get openssl to report back what it thinks are the values of the Key and IV. So lets try this:

# echo -n '00000000000000000000000000000000' | xxd -p -r | openssl enc -aes-128-cbc -P -nosalt -K '80000000000000000000000000000000' -iv '00000000000000000000000000000000'
iv =00000000000000000000000000000000

Ok, thats good. They Key and IV values are what we were expecting.
By the way, for this sort of validation check, we don't want to use a salt, hence the use of the '-nosalt' option.
Right, lets remove the '-P' and see what data comes out:
# echo -n '00000000000000000000000000000000' | xxd -p -r | openssl enc -aes-128-cbc -nosalt -K '80000000000000000000000000000000' -iv '00000000000000000000000000000000' | hexdump -C
00000000 0e dd 33 d3 c6 21 e5 46 45 5b d8 ba 14 18 be c8 |.Ý3ÓÆ!åFE[غ..¾È|
00000010 fe 3d e6 e1 86 98 08 4f 63 de e5 04 42 ff 94 d2 |þ=æá...OcÞå.Bÿ.Ò|

Oh, that strange!
I'm trying to encrypt 16-bytes, but the output is 32 bytes long!
But the first 16 bytes of output looks correct!

The answer to this is padding. If you specify the '-nopad' option, then you get the expected 16 bytes of output:

echo -n '00000000000000000000000000000000' | xxd -p -r | openssl enc -aes-128-cbc -nopad -nosalt -K '80000000000000000000000000000000' -iv '00000000000000000000000000000000' | xxd -p

Good. Thats the CIPHERTEXT output we were expecting!

If you check 'man enc' you see that the '-nopad' option, disables standard block padding. And the man page also notes "All the block ciphers normally use PKCS#5 padding also known as standard block padding".

By the way, in the above tests, the IV is all-zeroes, so we can abbreviate the command like this:

echo -n '00000000000000000000000000000000' | xxd -p -r | openssl enc -aes-128-cbc -nopad -nosalt -K 80000000000000000000000000000000 -iv 0 | xxd -p

To finish off, lets try some decryption, reversing what we did above:
# echo -n '0edd33d3c621e546455bd8ba1418bec8' | xxd -p -r | openssl enc -aes-128-cbc -d -nosalt -K 80000000000000000000000000000000 -iv 0 -nopad | xxd -p

Ok, that looks good. I think I am begining to get the hang of this!

No comments: