Authenticated Encryption
If you're going to use a symmetric encryption algorithm like AES256 then it is necessary to insure that the encrypted data has not been tampered with.
High-Level View
This can be done by
- Generating a random IV (Initialization Vector) -- clearText sent to decrypting party.
- encrypting your data
- generating a MAC (Message Authentication Code) over your IV and encrypted data (using a secret key).
- Sending the MAC to the decrypting party so they can test for the same result.
Exact Process Is a Bit Confusing
The process is a bit confusing so here are the things I learned while encrypting a user's data properly. You can read more about authenticated encryption at wikipedia.
Not Difficult, But A Challenge to Find Information
It's not difficult to do, but it was difficult to find all the information in one place so I decided to write this up.
What We Need For Authenticated Encryption
Authenticated encryption requires a few things:
- ClearText data (data to be encypted)
- Random IV (initialization vector) which will be passed in the clear -- AES256 needs 16 bytes of random data
- MAC key - A secret key used to generate the MAC (Message Authentication Code) which is 16-32 bytes of data, kept secret between you & the receiver who will decrypt the data
- MAC The final Message Authentication Code - generated over the IV & encrypted data, using the MAC key (secret key) - This code will be passed to the receiver (decrypting party) in the clear. This MAC must be known so the receiver can compare it to the value they calculate to insure the IV & encrypted data have not been tampered with.
Which Items Do You Supply to Decrypting Party?
One of the main points of confusion is created by not knowing which items should be supplied to the decrypting party. If you aren't sure, you may feel like you are exposing data that a cracker could use to steal or corrupt the encrypted data.
Here's the exact list of what you must provide to the decrypting party and in what format (clearText or encrypted) they should be supplied. Note: When I say something is clearText it means that the item can be exposed to anyone. IE - the data element does not have to be encrypted.
- Encrypted Data -- obviously this is the secret data you don't want exposed. Note: Our encryptedData will be base64 encoded (after encryption) to make it easier to transmit over HTTP).
- IV (initialization vector) -- This is a clearText value and in our case we generate a random value (using the CryptoJS library). This will be 16 random bytes (128 bits) and will be used when encrypting the data to randomize the output so the encrypted data is more random and more secure. The format of the data you provide to the decrypting party will be 2-byte hex values like:
137b276b3975612498e225657140c5a1
- MAC -- This is the clearText Message Authentication Code which is clearText which will be used by the decrypting party to validate that the IV & Encrypted Data that you sent has not be altered or corrupted. It will look like:
57bfeffc96a32402266abe2035801e8f65cbd58fc9d88dceb591572acb1dca74
Example Data Transfer Object (DTO)
If you were to load all that data into a DTO (JSON Object) it might look something like the following:
{MAC:"57bfeffc96a32402266abe2035801e8f65cbd58fc9d88dceb591572acb1dca74", IV:"137b276b3975612498e225657140c5a1", enryptedData:"dGhpcyBpc24ndCByZWFsbHkgZW5jcnlwdGVkLCBidXQgaXMganVzdCBhbiBleGFtcGxl"}
We send the MAC first (as a human-readable convenience) because the decrypting party will take the IV and Encrypted data, apply the MAC secret key and attempt to generate the MAC again. If it matches the decrypting party knows that the encrypted data and IV have not been altered or corrupted.
Ok, finally let's see it in action with some code.
Sample Code
There's a lot to this so I have posted some sample code so you can try it at via JSFiddle.
You can try the code out, right now at JSFiddle: Encrypt / Decrypt at JSFiddle
What Would a Decrypting Party Do?
It is important to know what a decrypting party will do with this data.
The decrypting party will:
- Supply the (agreed upon) secret MAC-key
- Use the secret MAC-key to generate a MAC (using Hmac) over the IV & EncryptedData.
- Determine if the MAC they generate matches the MAC that you sent them (in the clear).
If MAC Doesn't Match?
If the MAC doesn't match the one that was sent to the decrypting party, then the decrypting party will know that one of the following is true:
- the Encrypted data has been altered or corrupted
- the IV has been altered or corrupted
- both the IV & encrypted data have been corrupted
This would allow the receiver to know the data could have been compromised and they should know that the data may nots be safe.
True Authenticated Encryption
In some world, it may be possible for a cracker to corrupt the data, generate a new IV and try to pass it to you as a decrypting party. Since the MAC will only match the original encrypted bytes and associated IV you will be protected from such a thing. This is Authenticated Encryption.
Since we are generating the MAC we know that we are safe from this issue & our encrypted data is safe.
Point Out a Few Items of Interest
I will just point out a few items of interest.
Generate The IV Every Time We Encrypt
Every time the user clicks the Encrypt button, we generate a new Random IV. This insures that the data is randomized and more secure.
These are the two lines of code we use to do that:
let randomBytes = CryptoJS.lib.WordArray.random(128/8).toString();
iv = CryptoJS.enc.Hex.parse(randomBytes);
That means, even if you leave your password & clear text data the same, that the output encrypted text (base64 encoded) will change every time. This is all part of the randomization of the data to keep it secure.
See That In Action
To see that in action:
- Type in a password
- Type in some clear text data (to be encrypted)
- Just button-mash the Encrypt button and you'll see the output encrypted data changes every time.
Encrypting the Data
We use the following code to encrypt the data using our password. CryptoJS is the library we are using to implement AES256. Note: You can see the resources (come from CDN) in the JSFiddle tab above.
message = CryptoJS.AES.encrypt(clearTextData, CryptoJS.enc.Hex.parse(hashedPwd),{iv: iv});
We call the encrypt() function and pass in: clearTextData - our original message we want to encrypt. Bytes of our password - our password was SHA256 hashed to keep it safe, now we parse it into hex bytes. iv object - an object containing a param named iv and our randomly generated iv bytes.
That's It & RFC
That's it. Check it out and post any questions or comments you have about anything in the article.
I've used this to encrypt data on browser client, ElectronJS client and more & post data to my WebAPI. Later, I have decrypted the data using my Android app (Kotlin-based app) and other web clients.