Encryption is one of the more obtuse things that we do as programmers, perhaps appropriately so. Regardless of the language, search results are guaranteed to provide a myriad of different strategies, each describing different methods, many of which are subtly flawed, subverting their security goals. As is Java’s nature, performing encryption requires more programmatic steps than higher level languages, increasing the difficulty for those using encryption in their application.
First, let’s take a look at what we’re going to do here. This post covers symmetric encryption, wherein a single key is used for encryption and decryption. In such a system there is a single thing, the encryption key, that must be kept secret and shared between anyone wanting to encrypt or decrypt data. In our example, we are also using a message authentication code (MAC), which requires its own key that must be secret & shared. Pieces of information like these are appropriately called shared secrets.
The MAC ensures that the outputs of our encryption (namely the ciphertext and initialization vector) have not been altered. It is theoretically possible to alter the initialization vector (IV) and ciphertext in concert to change the message without knowing the secret key. Additionally, either of those pieces could be manipulated so as to cause a fault when they are fed into the decryption system, causing an unexpected exception in your decryption system.
Generally, you should employ the highest strength encryption readily available. Java ships by default with a
security policy that complies with United States cryptography export control regulations
that limits the encryption algorithms & key lengths that can be used.
To get all of the available cryptographic tools you must install the Java Cryptography Extension Unlimited Strength Jurisdiction Policy Files.
Without these, you will get
InvalidAlgorithmParameterException because Java can’t find the requested
InvalidKeyException if the given key length exceeds that which is permitted under the
jurisdiction policy files. Look out for these misleading errors when deploying your code to new
Check out our basic-java-encryption repository to follow along at home.
The javax.crypto package deals exclusively in bytes. If you have strings you want to encrypt, you’ll need to represent them as bytes. For arbitrary things like your encryption keys, the easiest way is to store them as Base64 encoded strings and read or write them to disk using something like Commons Codec’s Base64.
If you’re dealing with strings of unknown stripe, be sure to use explicit encodings—given how opaque encryption programming is, the last thing you want is to get hung up because what you thought was a UTF-8 string was actually interpreted as US-ASCII.
You need a few different objects to perform encryption: the actual Cipher, the initialization vector, and the key. It’s also a good idea to generate a message authentication code (MAC) of the ciphertext, to protect against tampering with the ciphertext or IV that could be used to manipulate the Cipher object upon decryption.
The encryption flow looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
The ciphertext, IV, and MAC can be stored wherever you keep data—they can be serialized into a single object, for example—without any special considerations. The keys, however, are sensitive and should be stored securely.
Decryption is a similar flow, but in reverse. You start by checking that the stored MAC matches the ciphertext & IV that you want to decrypt, and if they do, then you decrypt.
Assuming the objects from above still exist:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
Play around with a working example by cloning our basic-java-encryption repository.
Bits & Baubles
If encrypting multiple things, you must call
doFinal() for each plaintext with a
new initialization vector each time.
Having explained this whole thing, the real answer to encryption is to abstract away as much as possible. If possible, use libraries like keyczar or Jasypt that will aid you in performing high-level cryptographic functions, while taking care of many of the minutiae for you.