Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / security / cryptography

Create Authenticated Encryption & Encrypt Your Data with AES256

5.00/5 (1 vote)
30 Jun 2022CPOL10 min read 8.5K   150  
Fully working sample code and explanation of everything necessary to create authenticated encryption with AES256.
Learn to Encrypt your data using AES256 & JavaScript using proper Authenticated Encryption. This is a symmetric password-based encryption which is very useful to protect data which can be posted to WebAPIs and later consumed by any other process (Android, web client, WinForm client, etc).

Image 1

Introduction

This article was inspired by one simple thing: the desire to create a WebAPI which allows easy, safe data storage.

Try It Out Online

Before we go any further, you can try it out and examine the code at AuthEncryptViaAES256 - JSFiddle - Code Playground[^].

I've also attached a zip file so you can download it. Or, you can get the code at my Github repo for this project: GitHub - raddevus/Aes256ViaJS: using AES256 via CryptoJS library to encrypt / decrypt data (includes Authenticated Encryption)[^].

The rest of this article will explain what is actually happening when you use the code.

About AES256 Algorithm Via JavaScript

All my code simply implements the AES256 algorithm which is documented further at: https://code.google.com/archive/p/crypto-js/[^].

Note: How do I know that the code provided in that library works? Because I've implemented the decryption side using C#, Java (and other languages) which have their own AES256 encryption libraries and those libraries decrypt the data which is encrypted by this JavaScript library.

Background

I recently began a series of articles related to this idea here at CodeProject (Why HTML5? Why Not? UI / UX Versus Storage Challenge (Part 1 of N)[^]).

Easy, Safe, Reliable Data Storage For Apps

The basic point is that you can do everything in Web-based technologies to create a complete App but there is still no easy, safe, reliable way to really save data and make it available to the user no matter where she runs your app.

I discovered how to use AES256 to encrypt your data and store it using my simple LibreStore WebAPI (more on the WebAPI coming soon).

How This Article is Different Than Others

However, while reading a book, Real-World Cryptography (Manning Publishers - via amazon)[^], I came upon a thing called Authenticated Encryption (which this article will explain in detail).

Authenticated Encryption is the true target we want when encrypting our data. I had never seen this explained anywhere. Instead, most articles just show you the technical aspects of getting your cleartext data encrypted using AES256. However, I've never seen an article explain all of the details of the IV (Initialization Vector), MAC (Message Authentication Code) and what can be cleartext and what must be encrypted or kept secret.

Two Challenges to Encryption

The two main challenges in symmetric encryption must be explained and as I see it, those two challenges are:

  1. knowing that you're doing it right - are you implementing the algorithm properly? Are you generating an IV for every encryption?
  2. What must be kept secret & what can be in cleartext? Can the IV be exposed to all or must it be kept secret? What items must be kept secret?

One Additional Challenge

There's actually one other technical challenge also, which I will touch upon in a future article:

Every language does AES256 Encryption and Decryption slightly differently: different APIs etc. in Java, JavaScript, C#, etc.

The point is that you may get your data encrypted properly using AES256 only to learn that your client (written in C#) has a totally different Encryption API that you have to learn to decrypt it.

Let's get started.

Authenticated Encryption

If you're going to use a symmetric encryption algorithm like AES256, then it is necessary to ensure that the encrypted data has not been tampered with. That is what Authenticated Encryption guarantees.

High-Level View

Authenticated Encryption can be achieved by:

  1. Generating a random IV (Initialization Vector) -- clearText sent to decrypting party
  2. Encrypting your data
  3. Generating a MAC (Message Authentication Code) over your IV and encrypted data (using a secret key)
  4. Sending the MAC to the decrypting party so they can test for the same result

Exact Process Is a Bit Confusing

The process of creating Authenticated Encryption 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 - 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:

  1. ClearText data - (data to be encrypted)
  2. Password - In our case, we are going to use a password which will be obtained by the user for the encryption. There is a lot to strong passwords so we will not discuss that here further. The point is that the decrypting party will use that secret also. And, yes, the password is SHA256 hashed before it is ever used, so no worries.
  3. Random IV (initialization vector) which will be passed in the clear -- AES256 needs 16 bytes of random data and this IV should be created every time you encrypt the data, even if the password or cleartext data has not changed.
  4. 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
  5. 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 ensure the IV & encrypted data have not been tampered with.

Which Items Do You Supply to Decrypting Party?

One of the main points of confusion in Real-World Cryptography 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 to keep your data safe.

  1. 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).
  2. 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
  3. 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:

JavaScript
{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 encryptedData 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.

The code we are focusing on here is the encryption code so let's take a look at the small Encrypt() function you'll find in my JavaScript (main.js).

Here's the entire encrypt() function which should be completely understandable because it is so simple.

JavaScript
function encrypt(clearTextData, hashedPwd, useIV){
    if (useIV){
      let randomBytes   = CryptoJS.lib.WordArray.random(128/8).toString();
      iv = CryptoJS.enc.Hex.parse(randomBytes);
      console.log(`iv : ${iv}`);
      // old method (wrong) which used first 16 bytes of key (hashed pwd)
      // CryptoJS.enc.Hex.parse(key);
    message = CryptoJS.AES.encrypt(clearTextData, 
              CryptoJS.enc.Hex.parse(hashedPwd),{iv: iv});
    console.log(`message.iv : ${message.iv}`);
    console.log(`message: ${message}`);
    console.log(`message.ciphertext ${message.ciphertext}`);
    console.log(`message.salt ${message.salt}`);
    }
  else{
    message = CryptoJS.AES.encrypt(clearTextData, hashedPwd );
  }
    return message.toString();
}

I'll explain a few of the details here.

encrypt() Parameters

  1. clearTextData - Obviously, just the cleartext that you want to encrypt for protection
  2. hashedPwd - The web client calls a method which takes the user's password & hashes it (SHA256). This is that hash.
  3. useIV - I wanted to be able to show the user the differences when the user decides not to generate an IV. The CryptoJS algorithm generates an IV for you. It is stored in returned message object after a call to Crypto.AES.encrypt().

You can see that I also console.log() a number of properties that are returned in the message object after the Crypto.AES.encrypt() method is called.

After Successful Encryption

After a successful encryption, the message object contains the encrypted data. Calling message.toString() returns the encrypted data as base64 encoded bytes. That's what you'll see output on the screen when you try it in my example. That makes it very easy to transmit the bytes over HTTP.

What Would a Decrypting Party Do?

It is important to know what a decrypting party will do with this data.

The decrypting party will:

  1. Supply the (agreed upon) secret MAC-key
  2. Use the secret MAC-key to generate a MAC (using Hmac) over the IV & EncryptedData.
  3. 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:

  1. The Encrypted data has been altered or corrupted.
  2. The IV has been altered or corrupted.
  3. 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 not 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.

Now I'll wrap this up by pointing out a few items of interest.

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 ensures that the data is randomized and more secure. However, it also means you have to share the new IV with the decrypting party.

These are the two lines of code we use to do that:

JavaScript
let randomBytes   = CryptoJS.lib.WordArray.random(128/8).toString();
iv = CryptoJS.enc.Hex.parse(randomBytes);

That means, even if you leave your password and clear text data the same, 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:

  1. Type in a password
  2. Type in some clear text data (to be encrypted)
  3. 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.

JavaScript
message = CryptoJS.AES.encrypt(clearTextData, 
             CryptoJS.enc.Hex.parse(hashedPwd),{iv: iv});

We call the encrypt() function and pass in:

  1. clearTextData - our original message we want to encrypt
  2. hashedPwd - our password was SHA256 hashed to keep it safe, now we parse it into hex bytes
  3. 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 really hope this article serves to open up symmetric encryption with AES256 because it is a very valuable system that seems to get less attention than I expect. Also, I've not seen all of this together in one place and I hope you find it valuable.

I've used this to encrypt data on browser client, ElectronJS client and more & post data to my WebAPI. Later, I decrypted the data using my Android app (Kotlin-based app) and other web clients.

History

  • 30th June, 2022: First publication

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)