OpenSSL provides industrial-grade encryption for many web services. It is supported on many platforms (linux, Windows, etc), with many language interfaces, and even commands to generate encryption keys.
While preparing this page, the original intent was to show the API; indeed such packages as M2Crypto (for Python) and BouncyCastle (for Java) are OpenSSL-compatible. I was able to get messages encrypted and decrypted with RSA using both Python and Java based on OpenSSL-generated public and private keys in PEM format. Unfortunately, these packages aren't installed on the CSG computers and there isn't a trivial way to install them (for Java it's almost trivial, for for Python, dependencies on OpenSSL are tricky).
Thus, instead of using the API, the explanation below refers only to using the command-line syntax provided with OpenSSL. Installing OpenSSL on Windows also install the commands, so they are accessible by console (perhaps also needing to set up environmental PATH variables).
Examples of OpenSSL Commands
See OpenSSL Command-Line How To for many details and options beyond the narrative that follows.
Suppose there is a file secret.txt that needs to be sent securely over the network. Here is a script illustrating the basic ideas:
openssl genrsa -out privkey.pem 256 openssl rsa -in privkey.pem -out pubkey.pem -pubout openssl rand 32 -out deskey -base64 openssl enc -e -des -kfile deskey -in secret.txt -out hidden openssl enc -e -base64 -in hidden -out hidden.txt openssl rsautl -encrypt -pubin -inkey pubkey.pem -in deskey -out rsadeskey openssl enc -e -base64 -in rsadeskey -out rsadeskey.txt # pretend three things are transmitted, privkey.pem, hidden.txt and rsadeskey.txt openssl enc -d -base64 -in hidden.txt -out junk openssl enc -d -base64 -in rsadeskey.txt -out junkkey openssl rsautl -decrypt -inkey privkey.pem -in junkkey -out junkdeskey openssl enc -d -des -kfile junkdeskey -in junk -out message.txt
There's quite a lot going on here, and it's best to break things down, a few steps at a time:
- The first two lines create a private and public pair for RSA-style encryption:
openssl genrsa -out privkey.pem 256 openssl rsa -in privkey.pem -out pubkey.pem
The number 256 is the number of bits in the private key to generate. Using 256 is not very secure by today's standards. Using 4096 is more secure, but more time-consuming. The best practical number to use changes over the decades. If you look at the privkey.pem file generated, you might notice it is larger than the pubkey.pem file. That's because privkey.pem has all the information (number of bits in key, the prime numbers used in the generation process, and so on). The public, by contrast, only has the public key. That's why pubkey.pem can be produced from privkey.pem (which ordinarily would not be feasible). Unfortunately, the PEM standard does not have a way to purely isolate what textbooks would call the "private key" in a way that knowing just the private key would allow the public key to be guessed. Practically speaking, the only reason to encrypt using a private key would be to authenticate the message. The openssl command does have a sign option, which uses the private key to generate a signature of a message, which can then be used to authenticate.
Because RSA encryption is so expensive, it's a better idea to use an efficient, non-public key encryption technique for large data (secret.txt might be large). The next three lines create a random key of 32 bytes, convert it from binary to readable (Base64) format, put that into a file deskey, and then using the DES algorithm (Data Encryption Standard) our secret.txt is encrypted. Also, to make the encrypted file into normal-looking ASCII so that it is convenient to show and send in email on through the web, the third statement encodes the encrypted version of secret.txt into hidden.txt, using Base64 encoding:
openssl rand 32 -out deskey -base64 openssl enc -e -des -kfile deskey -in secret.txt -out hidden openssl enc -e -base64 -in hidden -out hidden.txt
- We are almost ready to send the encrypted file over the network. The problem is this: if we transmit the DES key (deskey) over the network, anyone can decrypt hidden.txt. The trick is now to encrypt deskey using RSA. Since deskey is relatively short (was only 32 bytes in binary) the RSA algorithm won't take much time to run:
openssl rsautl -encrypt -pubin -inkey pubkey.pem -in deskey -out rsadeskey openssl enc -e -base64 -in rsadeskey -out rsadeskey.txtThe "rsautl" option of the openssl command is used above to encrypt the contents of file deskey (that is, the DES key), producing rsadeskey. Ideally, the RSA algorithm would use the private key, in privkey.pem, to do this encryption. However, openssl doesn't have this option. In practice, each side of a communication would have its won private/public key pair, exchange public keys, and the DES key would be encrypted using the public key of the other side. We are constrained by what openssl can do, hence the (admittedly lame) step above. Finally, since the file rsadeskey is binary, it can be more convenient to encode it using Base64, which is what the second command above does.
- Now pretend that these three files are transmitted by email or published on a social network, or messaged to some destination: privkey.pem, hidden.txt, and rsadeskey.txt. These are all ASCII files (not binary). In real life, only pubkey.pem would be shared, but to simplify things and overcome openssl limitations, we have to share the private key file.
- Time to decrypt! The first step after saving the files is to convert the DES key and the encrypted file back into binary. These two commands do that:
openssl enc -d -base64 -in hidden.txt -out junk openssl enc -d -base64 -in rsadeskey.txt -out junkkeyFiles "junk" and "junkkey" are temporary files.
- The next step is to use RSA and the public key to decrypt junkkey, which will recover the original value of deskey:
openssl rsautl -decrypt -inkey privkey.pem -in junkkey -out junkdeskeyNotice how the "rsautl" option, with "-decrypt", used the private key in privkey.pem. After this, junkdeskey has the original value of deskey.
- Finally, we can do the DES decryption:
openssl enc -d -des -kfile junkdeskey -in junk -out message.txtUsing junkdeskey as the des key, the binary data in junk is decrypted by DES and produces message.txt, which is identical to secret.txt
Instead of using DES by the "-des" option, we could have used any of these flavors of cipher encryption:
-aes-128-cbc -aes-128-cfb -aes-128-cfb1 -aes-128-cfb8 -aes-128-ecb -aes-128-ofb -aes-192-cbc -aes-192-cfb -aes-192-cfb1 -aes-192-cfb8 -aes-192-ecb -aes-192-ofb -aes-256-cbc -aes-256-cfb -aes-256-cfb1 -aes-256-cfb8 -aes-256-ecb -aes-256-ofb -aes128 -aes192 -aes256 -bf -bf-cbc -bf-cfb -bf-ecb -bf-ofb -blowfish -cast -cast-cbc -cast5-cbc -cast5-cfb -cast5-ecb -cast5-ofb -des -des-cbc -des-cfb -des-cfb1 -des-cfb8 -des-ecb -des-ede -des-ede-cbc -des-ede-cfb -des-ede-ofb -des-ede3 -des-ede3-cbc -des-ede3-cfb -des-ede3-cfb1 -des-ede3-cfb8 -des-ede3-ofb -des-ofb -des3 -desx -desx-cbc -rc2 -rc2-40-cbc -rc2-64-cbc -rc2-cbc -rc2-cfb -rc2-ecb -rc2-ofb -rc4 -rc4-40
The important thing is to use the same flavor on both ends.
Public/Private or Private/Public
Remarkably, encryption and decryption are sensible in any combination of public and private, so long as opposites are used on both ends. The openssl API allows for both directions, though the openssl command is not so flexible. So, if you publish something with your private key and I know your public key, I can be confident it came from you -- though signing is likely the recommended way to authenticate. If I send you something encrypted with your public key, I can be confident that only you are able to decrypt it (unless you share your private key with others). The big open problem is how to know whether some file pubkey.pem really does come from you!