If you need a quick way to encrypt and decrypt a file, you can use the openssl tool of the OpenSSL library. While its man page seems daunting at first, its use is rather simple. This post contains step-by-step instructions how to use openssl’s symmetric ciphers to achieve a simple level of confidentiality.

First steps with encryption

Let’s assume that Alice wants to encrypt a file plaintext.txt using a strong symmetric cipher like Triple DES. This file may contain anything Alice wants, be it binary or text. For the sake of this example, it will contain a single line:

(alice) $ echo 'this is the plain text' > plaintext.txt

To encrypt this file, all Alice has to do is to call the openssl enc command with the -e (encrypt) flag, specifying the required algorithm (-des3), the input file (-in) and an output file (-out).

(alice) $ openssl enc -e -des3 -salt -in plaintext.txt -out ciphertext.bin
enter des-ede3-cbc encryption password:
Verifying - enter des-ede3-cbc encryption password:

Here, Alice used the password “cryptme” (without the quotes), which was not echoed to the console.

The result of this command is the file ciphertext.bin. Because it is a binary file, Alice can examine it with a hexdump tool, instead of outputting it to the console with cat (which could have scrambled the console):

(alice) $ hd ciphertext.bin 
00000000  53 61 6c 74 65 64 5f 5f  f9 d1 8f 21 17 bf 5f f3  |Salted__...!.._.|
00000010  25 c8 0c c8 8c 84 68 87  cb 40 64 59 68 08 ae 05  |%.....h..@dYh...|
00000020  7c b1 c8 51 b3 f4 3b d1                           ||..Q..;.|

As we can see, the result is a binary file that looks rather scrambled.

So how can Bob decrypt ciphertext.bin, assuming he knows the password? He’ll simply use openssl enc with the -d (decrypt) flag, and reverse the order of input (-in) and output (-out) files. In this case, Bob will select plaintext2.txt as the name of the (hopefully) decrypted text, so that we can compare plaintext.txt and plaintext2.txt later:

(bob) $ openssl enc -d -des3 -in ciphertext.bin -out plaintext2.txt
enter des-ede3-cbc decryption password:

(bob) $ cat plaintext2.txt 
this is the plain text

Here, Bob entered the same password “cryptme” and same symmetric cipher (-des3).

Someone with access to both plaintext.txt and plaintext2.txt could use the Unix command diff to compare both files. If diff keeps silent — as it does here — we’ll know that both files are indeed identical:

(carol) $ diff plaintext.txt plaintext2.txt

As you can see, we’ve got back the original plain text.

Out of the blue, Plod comes along and wants to decrypt ciphertext.bin. Unless he managed to extract the password (“cryptme“) out of Alice or Bob, he will not be able to reconstruct the plain text without a rather daunting brute force attack against Triple DES. Here’s an example of an unsuccessful interaction of Plod with openssl enc and the wrong password:

(plod) $ openssl enc -d -des3 -in ciphertext.bin -out plaintext3.txt
enter des-ede3-cbc decryption password:
bad decrypt
34380839224:error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt:/usr/src/crypto/openssl/crypto/evp/evp_enc.c:529:

(plod) $ hd plaintext3.txt 
00000000  83 f2 f1 fa 00 44 ba 7b  f4 07 44 2e 5a cc df 9d  |.....D.{..D.Z...|

(plod) $ rm plaintext3.txt

Not only didn’t Plod get back the original plain text (plaintext3.txt doesn’t contain the string “this is the plain text”), openssl also threw a bad decrypt error. This is expected: Triple DES is a symmetric cipher: if you don’t provide the same password to decrypt the file, you can’t expect to get the original plain text file back… which is of course the whole point of encryption.

Base64 encoding

The ciphertext ciphertext.bin that Alice created above was a binary file. If she wanted to email it to Bob, it should probably be Base64-encoded. Most MUAs (email clients) will base-64 encode attachments on-the-fly, but if you prefer, you can also let openssl base64 to do the job. Use -e (encrypt) to base-64 encode, and -d (decrypt) to base64-decode an (-in) input file into an (-out) output file:

(alice) $ openssl base64 -e -in ciphertext.bin -out ciphertext.asc

(alice) $ cat ciphertext.asc

(alice) $ openssl base64 -d -in ciphertext.asc -out ciphertext2.bin

(alice) $ diff ciphertext.bin ciphertext2.bin

(alice) $ rm ciphertext.asc ciphertext2.bin

Alice first base-64 encoded ciphertext.bin into ciphertext.asc using the subcommand “openssl base64” with the -e flag. The file ciphertext.asc contains only ASCII code, and can thus be displayed safely with the UNIX command cat, without fear of scrambling the console. The reverse operation was to base64-decode ciphertext.asc into ciphertext2.bin. Again, Alice used the openssl base64 command, but this time with the -d flag to reverse directions (of course swapping -in and -out along the way and selecting a second file ciphertext2.bin for the base64-decoded cipher text). Finally Alice verified that ciphertext.bin and ciphertext2.bin are indeed the same with the UNIX command diff. Since diff didn’t output anything, Alice can be sure that both files contain the same cipher text.

Using openssl enc, followed by openssl base64 is somewhat cumbersome. The -a flag (armor) of openssl enc will automatically base64-encode the result of encryption (-e) and base64-decode an input file prior to decryption (-d).

Repeating the prior example with -a:

(alice) $ openssl enc -e -a -des3 -salt -in plaintext.txt -out ciphertext.asc
enter des-ede3-cbc encryption password:
Verifying - enter des-ede3-cbc encryption password:

(alice) $ cat ciphertext.asc

(bob) $ openssl enc -d -a -des3 -in ciphertext.asc -out plaintext2.txt
enter des-ede3-cbc decryption password:

(bob) $ cat plaintext2.txt
this is the plain text

Notice that no intermediate ciphertext.bin was created here. If we needed it anyway, we could always create it with openssl base64 -d out of ciphertext.asc as we’ve shown above.

Acquiring passwords

The biggest problem with our previous example was that we had to type in the password directly. This is more than adequate for one-shot encryptions and decryptions, but if you need to encrypt thousands of files, or if you expect to use openssl in a script, manually entering a password for every single file is not really all that practical.

Fortunately, openssl provides other ways to input a password, using the -pass flag:

(bob) $ openssl enc -d -des3 -in ciphertext.bin -out plaintext2.txt -pass pass:cryptme 

(bob) $ cat plaintext2.txt
this is the plain text

(bob) $ rm plaintext2.txt

Using the syntax pass:password, Bob could simply provide the password on the command line. Of course, this is VERY insecure, because everyone could peek at the password using the Unix command ps at the right moment. Furthermore, the password can usually be found in Bob’s shell’s history, which the shell usually saves into a dot file of his home directory.

A slightly less insecure way is to store the password in an environment variable and to pass the name of that environment variable with the env:var syntax to openssl. Using csh, Bob stores the password into the environment variable MYPASS like this:

(bob) % setenv MYPASS cryptme

(bob) % openssl enc -d -des3 -in ciphertext.bin -out plaintext2.txt -pass env:MYPASS

(bob) % cat plaintext2.txt
this is the plain text

(bob) % rm plaintext2.txt

(bob) % unsetenv MYPASS

With sh syntax, Bob would rather do:

(bob) $ MYPASS=cryptme; export MYPASS

(bob) $ openssl enc -d -des3 -in ciphertext.bin -out plaintext2.txt -pass env:MYPASS

(bob) $ cat plaintext2.txt
this is the plain text

(bob) $ rm plaintext2.txt

(bob) $ MYPASS=''

In both cases, this too is not very secure, because some versions of UNIX can show the environment of another process (e.g. with ps), therefore exposing the password to prying eyes. Additionally, don’t forget that in this particular example, the shell also stores all commands, including the password, into its history file. A real application would set up the environment in the process with setenv(3), and then fork the openssl command directly, bypassing the shell (not shown here).

Another alternative is to store the password in a file, and make sure the file has just enough permissions but no more that absolutely necessary, and then fetch it with the file:pathname syntax:

(bob) $ echo 'cryptme' > thepassword.dat

(bob) $ chmod 400 thepassword.dat

(bob) $ openssl enc -d -des3 -in ciphertext.bin -out plaintext2.txt -pass file:thepassword.dat

(bob) $ cat plaintext2.txt
this is the plain text

(bob) $ rm -f plaintext2.txt thepassword.dat

Please note that unless Bob has the right umask, there’s a small window of opportunity between file creation and chmod, where thepassword.dat is readable by others.

Use this option with care: the password is left unencrypted on disk: anyone with access to the disk (root, or anyone with physical access to the drive) will be able to get the password and decrypt ciphertext.bin with it.

You can also write a program that spawns (forks) an openssl process. In this case, you have yet another way to pass a password from that program to openssl. Just use the fd:number syntax (not shown here).

Finally, you can also use the stdin syntax to pass the password via standard input:

(bob) $ echo 'cryptme' | openssl enc -d -des3 -in ciphertext.bin -out plaintext2.txt -pass stdin

(bob) $ cat plaintext2.txt
this is the plain text

Of course, in this special case, this is just as insecure as using the pass:password syntax. The intended use is to call openssl with the stdin syntax from another program via a pipe (which we won’t show here).

Passwords, Keys and IVs

You’ve probably noticed that Alice used the symmetric Triple DES cipher algorithm (-des3) to encrypt plaintext.txt and Bob used the same algorithm to decrypt ciphertext.bin (or ciphertext.asc). Or, to be more precise, Alice and Bob used Triple DES is CBC mode. Instead of des-ede3-cbc, Alice and Bob could have used any other symmetric cipher in their allowed modes.

By the way, this is a list of available cipher commands:

$ openssl list-cipher-commands

Depending on how openssl and its underlying library OpenSSL were build on your system, the list may also contain additional ciphers like IDEA. Recommended ciphers are the current AES standard with a key length of 256 bits 128 bits in CBC mode (aes-256-cbc aes-128-cbc) [update (07/31/2009): see here why 256-bit AES may have more flaws than 128 bits AES], but the more conservative Triple DES mode (des-ede3-cbc) has received a fair amount of scrutiny over decades. Other symmetric ciphers like Blowfish (bf), RC4 and RC5 have also been around for quite a while, and are highly regarded as well. Caveat emptor: a symmetric cipher is as secure as its key length: you’ll need to avoid ciphers with key lengths crippled to 40 bits , that were in use when the US still had restrictions on the export of strong ciphers.

Most of those symmetric ciphers expect a key of fixed bit length, though the lengths and other requirements for the keys vary from cipher to cipher and mode to mode. Some cipher/mode combinations also require an initialization vector (IV), that also has special mathematical requirements. For additional security, a salt may also be provided to further randomize the keys and IVs.

While it is possible to enter raw keys, IVs and the salt on the openssl command line with the -K, -iv, and -S flags respectively (using hexadecimal notation), it is not recommended, because it is too easy to inadvertently provide weak or outright invalid parameters. Providing a password (pass phrase) is preferable, because openssl automatically derives the appropriate key and IV for the selected cipher/mode out of the password in a manner that is believed to be cryptographically secure. That’s exactly what Alice and Bob did above: “cryptme” was merely the password that openssl used to derive a key and IV of appropriate bit length, which together with a salt were being fed to the Triple DES algorithm.

Of course, the strength of the whole application now rests heavily on the selection of a good and truly unguessable password. But this is another can of worms.  It’s enough to say that small passwords like “cryptme” are too easily guessable with brute force attacks, and not secure at all. A long phrase, with a mix of letters, and misspelled words is probably already better, as long as you throw in enough random cruft. Use your own judgement to select a good password / pass phrase. You can also use dices to generate fairly good, memorable pass phrases with enough entropy.

What we’ve not covered here

Alice can safely  email ciphertext.bin (or the base64-encoded equivalent ciphertext.asc) to her friend Bob over the Internet, but Bob will need to know the password beforehand in order to decrypt it. What Alice should NEVER do is to email Bob that password, because anybody in the middle (Eve) could intercept both emails, recover the password which Alice  sent unencrypted and use it to decrypt the cipher text. If at all, Alice needs to give Bob the password only over a secure channel (i.e. when meeting personally).

Furthermore, the cipher text could get corrupted in transit, whether accidentally or on purpose. If Mallory somehow gained access to the password from previous communications between Alice and Bob, she could easily intercept ciphertext.bin, and decrypt it with that password. Then she can set out to modify the plain text in a malicious kind of way (e.g. change an account number). Finally, she can reencrypt the modified plain text with the compromised password, and send it along to Bob. Bob wouldn’t be able to detect anything, and may wrongly assume that just because he was able to decrypt the message, that this message really came from Alice and was not modified.

Both problems (key agreement over an insecure channel, integrity checks with signatures) are easily solved with public key cryptography, which I’ll cover in another post.

But for simple applications like, say, locally encrypting your (backup) files, using openssl with symmetric ciphers is usually adequate… The truly paranoid would however augment the encrypted file with a MAC (message authentication code) to prevent undetected tampering. Because MACs require public key algorithms, I’ll cover them in another post.


  1. Robin Zeherquist

    You really make it seem so easy with your presentation but I find this topic to be really something which I think I would never understand. It seems too complicated and very broad for me. I am looking forward for your next post, I will try to get the hang of it!

  2. I know this is really boring and you are skipping to the next comment, but I just wanted to throw you a big thanks – you cleared up some things for me!

  3. Maragret Licavoli

    Hi just stumbled your blog and have been reading, do you also run another a pet related blog that looks exactly like this one?

  4. fabrizio carraro

    You clarified me many things not obvious from the openssl manuals. But a problem is still making me mad. I’m trying to decrypt an image crypted with aes128 following the DCI (digital cinema) rules. I don’t know what block cipher mode DCI uses, and if I need the IV. I have only the key used to
    crypt the image. Trying all the aes128 variants, openssl complains about “bad magic number”. If I try to put any IV (all zeros or all ffffs) most modes like it, I get no errors but the image is still garbage. Is there any way to understand which is the correct decoding mode? I think just the key is being used in the DCI specs, but not sure. Can I try to read the magic number of the encrypted file and understand something? I understand that the string “salted__” is not encrypted and should be there, but there is nothing like that in the first bytes of the image. Any hint? Thanks for your time! Fabrizio

  5. Hi Fabrizio,

    I didn’t delve into the kind of encryption used by DCI yet. Perhaps someone else can help?