Demo of Symmetric Key Encryption using OpenSSL

The following is an example of using OpenSSL in Ubuntu Linux to perform symmetric key encryption. DES with ECB mode of operation is used. I have a similar demo of OpenSSL for DES encryption as a screencast. Also I have some different examples of encryption in my article introducing OpenSSL. In the following I do not attempt to explain the steps or commands in detail, just give a trace so others can repeat and learn themselves.

Before getting started with creating messages, lets output the versions of Ubuntu, the Linux kernel and OpenSSL being used.

$ pwd
/home/sgordon/demo
$ lsb_release -a
No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 13.10
Release:	13.10
Codename:	saucy
$ uname -a
Linux potato 3.11.0-13-generic #20-Ubuntu SMP Wed Oct 23 07:38:26 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux
$ openssl version
OpenSSL 1.0.1e 11 Feb 2013

We need a plaintext message to encrypt. The meaning of the following output is:

  1. Create a short text message with echo. The -n option is used to ensure no newline is added to the end. There are two things about this message that will be important later: the length is a multiple of 8 characters (9 by 8 characters) and the word secret appears twice (in particular positions).
  2. Display the message on the screen with cat.
  3. Count the number of characters with wc.
  4. View the file size with ls.
  5. Show the message in hexadecimal and binary using xxd. From now on, I'll only look at the hexadecimal values (not binary).
$ echo -n "Hello. This is our super secret message. Keep it secret please. Goodbye." > plaintext.txt
$ cat plaintext.txt 
Hello. This is our super secret message. Keep it secret please. Goodbye.$ (ENTER)
$ wc -m plaintext.txt 
72 plaintext.txt
$ ls -l
total 4
-rw-r--r-- 1 sgordon sgordon 72 Nov 11 16:39 plaintext.txt
$ xxd -c 8 plaintext.txt 
0000000: 4865 6c6c 6f2e 2054  Hello. T
0000008: 6869 7320 6973 206f  his is o
0000010: 7572 2073 7570 6572  ur super
0000018: 2073 6563 7265 7420   secret 
0000020: 6d65 7373 6167 652e  message.
0000028: 204b 6565 7020 6974   Keep it
0000030: 2073 6563 7265 7420   secret 
0000038: 706c 6561 7365 2e20  please. 
0000040: 476f 6f64 6279 652e  Goodbye.
$ xxd -b -c 8 plaintext.txt 
0000000: 01001000 01100101 01101100 01101100 01101111 00101110 00100000 01010100  Hello. T
0000008: 01101000 01101001 01110011 00100000 01101001 01110011 00100000 01101111  his is o
0000010: 01110101 01110010 00100000 01110011 01110101 01110000 01100101 01110010  ur super
0000018: 00100000 01110011 01100101 01100011 01110010 01100101 01110100 00100000   secret 
0000020: 01101101 01100101 01110011 01110011 01100001 01100111 01100101 00101110  message.
0000028: 00100000 01001011 01100101 01100101 01110000 00100000 01101001 01110100   Keep it
0000030: 00100000 01110011 01100101 01100011 01110010 01100101 01110100 00100000   secret 
0000038: 01110000 01101100 01100101 01100001 01110011 01100101 00101110 00100000  please. 
0000040: 01000111 01101111 01101111 01100100 01100010 01111001 01100101 00101110  Goodbye.

To encrypt with DES-ECB we need a secret key (as well as an initialisation vector). You can choose your own values. For security, they should be randomly chosen. There are different ways to generate a random value in Linux. Three are demonstrated below.

  1. The Bash shell has a built-in random number generator, which is accessed from the shell variable $RANDOM. It uses a Linear Congruential Generator to return a value between 0 and 32,767. This is not a cryptographically strong PRNG and should NOT be used to keys. I include it here only as an example; I do not use the output. (To see the details of the LCG algorithm used, look in the Bash source code; after downloading and unpackaging the source, look in the file variables.c, search for the function brand. You can also see that the seed is based on the current time and process ID.)
  2. The Linux kernel has a pseudo-device /dev/urandom which is considered cryptographically strong PRNG for most applications.
  3. OpenSSL has its own PRNG which is also considered cryptographically strong.
$ echo $RANDOM
4086
$ cat /dev/urandom | xxd -l 8 -b
0000000: 10000111 11110111 01001101 10011100 01111110 10110110  ..M.~.
0000006: 01010110 11010001                                      V.
$ cat /dev/urandom | xxd -l 8 -g 8
0000000: a499056833bb3ac1                   ...h3.:.
$ openssl rand 8 -hex
001e53e887ee55f1

Encrypt the plaintext using DES-ECB. The IV and Key are taken from the outputs of /dev/urandom and OpenSSL PRNG above.

$ openssl enc -des-ecb -e -in plaintext.txt -out ciphertext.bin -iv a499056833bb3ac1 -K 001e53e887ee55f1 -nopad

Now look at the output ciphertext. First note it is the same length as the plaintext (as expected, when no padding is used). And on initial view, the ciphertext looks random (as expected). But closer inspection you see there is some structure: the 4th and 7th lines of the xxd output are the same. This is because it corresponds to the encryption of the same original plaintext " secure " (recall that word was repeated in the plaintext, in the positions such that it is in a 64-bit block). Since ECB is used, repetitions in input plaintext blocks will result in repetitions in output ciphertext blocks. This is insecure (especially for long plaintext). Another mode of operation, like CBC, should be used.

$ ls -l
total 8
-rw-r--r-- 1 sgordon sgordon 72 Nov 11 16:42 ciphertext.bin
-rw-r--r-- 1 sgordon sgordon 72 Nov 11 16:39 plaintext.txt
$ xxd -c 8 ciphertext.bin 
0000000: 56dc b368 d9ef 0793  V..h....
0000008: 7be4 a87d e26d c2f1  {..}.m..
0000010: e042 bbe6 9e00 6d37  .B....m7
0000018: f1e9 7163 cb4a 38d8  ..qc.J8.
0000020: 5394 a92f 8cf2 ac72  S../...r
0000028: 5064 be07 f67c d807  Pd...|..
0000030: f1e9 7163 cb4a 38d8  ..qc.J8.
0000038: a31c 0efd cd0b dd03  ........
0000040: 0486 7e2d 00ad 762d  ..~-..v-

Now lets decrypt:

$ openssl enc -des-ecb -d -in ciphertext.bin -out received.txt -iv a499056833bb3ac1 -K 001e53e887ee55f1 -nopad

And look at the decrypted value. Of course, it matches the original plaintext message.

$ ls -l
total 12
-rw-r--r-- 1 sgordon sgordon 72 Nov 11 16:42 ciphertext.bin
-rw-r--r-- 1 sgordon sgordon 72 Nov 11 16:39 plaintext.txt
-rw-r--r-- 1 sgordon sgordon 72 Nov 11 16:43 received.txt
$ cat received.txt 
Hello. This is our super secret message. Keep it secret please. Goodbye.$ (ENTER)
$ xxd -c 8 received.txt 
0000000: 4865 6c6c 6f2e 2054  Hello. T
0000008: 6869 7320 6973 206f  his is o
0000010: 7572 2073 7570 6572  ur super
0000018: 2073 6563 7265 7420   secret 
0000020: 6d65 7373 6167 652e  message.
0000028: 204b 6565 7020 6974   Keep it
0000030: 2073 6563 7265 7420   secret 
0000038: 706c 6561 7365 2e20  please. 
0000040: 476f 6f64 6279 652e  Goodbye.

Now lets try and decrypt again, but this time using the wrong key. I've changed the last hexadecimal digit of the key from "1" to "2".

$ openssl enc -des-ecb -d -in ciphertext.bin -out received2.txt -iv a499056833bb3ac1 -K 001e53e887ee55f2 -nopad

Looking at the decrypted message, it is random. We didn't obtain the original plaintext. Normally, when padding is used, OpenSSL adds a checksum when encrypting which allows, after decrypting, incorrect deciphered messages to be automatically detected.

$ ls -l
total 16
-rw-r--r-- 1 sgordon sgordon 72 Nov 11 16:42 ciphertext.bin
-rw-r--r-- 1 sgordon sgordon 72 Nov 11 16:39 plaintext.txt
-rw-r--r-- 1 sgordon sgordon 72 Nov 11 16:46 received2.txt
-rw-r--r-- 1 sgordon sgordon 72 Nov 11 16:43 received.txt
$ xxd -c 8 received2.txt 
0000000: 0346 e59e c22d 403f  .F...-@?
0000008: 63ff 28fd eb6b 387d  c.(..k8}
0000010: b52f d595 06c0 342f  ./....4/
0000018: f419 3569 e383 c857  ..5i...W
0000020: 0a77 0b49 6f62 cb64  .w.Iob.d
0000028: 8265 d419 51f3 ea12  .e..Q...
0000030: f419 3569 e383 c857  ..5i...W
0000038: f296 33f3 5cf4 d359  ..3.\..Y
0000040: e205 4018 0ce0 34f5  ..@...4.