Our website is made possible by displaying online advertisements to our visitors. Please consider supporting us by disabling your ad blocker.

Implement AES Strength Encryption With JavaScript

TwitterFacebookRedditLinkedInHacker News

I recently started playing around with the Dropbox Datastore JavaScript API and decided that I wanted an extra layer of security in the data I store on the Dropbox server. I figured the best way to accomplish this would be to encrypt all data in my application before syncing.

It took me a while to find an encryption library that I liked, but in the end, I chose the JavaScript library Forge. This library has plenty of cryptography tools that extend beyond just AES encryption, thus making it very worthy to check out.

To use Forge, I highly recommend you install NPM because it is one of the only ways to get a minified browser compatible version of the project. With NPM installed, run the following to install Forge:

npm install node-forge

If you’re using Mac or Linux, you may need to include a sudo to install as a privileged user.

The next step is to download the full Forge Git repository so that we can run some scripts for minification. Using a terminal or command prompt, navigate to the root directory of the Forge project and run:

npm install
npm run minify

The above two commands will produce a minified js/forge.min.js file that we can include in any HTML based project.

When working with cryptography, there are a few things you should familiarize yourself with.

Salts via Wikipedia:

In cryptography, a salt is random data that is used as an additional input to a one-way function that hashes a password or passphrase. The primary function of salts is to defend against dictionary attacks versus a list of password hashes and against pre-computed rainbow table attacks.

Initialization Vectors (IV) via Wikipedia:

In cryptography, an initialization vector (IV) or starting variable (SV) is a fixed-size input to a cryptographic primitive that is typically required to be random or pseudorandom. Randomization is crucial for encryption schemes to achieve semantic security, a property whereby repeated usage of the scheme under the same key does not allow an attacker to infer relationships between segments of the encrypted message.

With this in mind, we are going to make use of AES-CBC 128 bit encryption ciphers. To start things off, lets go ahead and create a key based upon a password phrase and a salt:

var salt = forge.random.getBytesSync(128);
var key = forge.pkcs5.pbkdf2("Pass Phrase", salt, 40, 16);

With our cipher key generated, we now need to create a random initialization vector.

var iv = forge.random.getBytesSync(16);

The salt, pass phrase, and initialization vector will be required for both encryption and decryption. They must remain consistent for a successful conversion, so it is best to store the salt and iv alongside the encrypted cipher text.

Now for the fun stuff. We are now going to encrypt a string of text with the purpose of storing it in a database.

var cipher = forge.cipher.createCipher('AES-CBC', key);
cipher.start({iv: iv});
cipher.update(forge.util.createBuffer("Text to be encrypted"));
cipher.finish();
var cipherText = forge.util.encode64(cipher.output.getBytes());

The above code will use our salted key and initialization vector to encrypt a string of text. By default, the cipher text will be randomness that we probably shouldn’t store in a datastore. Instead we are using forge.util.encode64 to make it Base64 encoded. This Base64 encoded cipher text along with a Base64 version of the salt and iv, should be stored in your datastore. If you’re using AngularJS, you can do something like this to store the encrypted string in local storage:

var myObj = {
    cipher_text: cipherText,
    salt: forge.util.encode64(salt),
    iv: forge.util.encode64(iv)
};

window.localStorage.setItem("encrypted_item", JSON.stringify(myObj));

Now that we have our encrypted item stored in a database, it is time to decipher it for use. The steps are going to be similar, with the exception we are going to re-use the salt and iv.

var salt = forge.util.decode64("[STORED_SALT_VALUE_HERE]")
var iv = forge.util.decode64("[STORED_IV_VALUE_HERE]");
var key = forge.pkcs5.pbkdf2("Pass Phrase", salt, 40, 16);
var decipher = forge.cipher.createDecipher('AES-CBC', key);
decipher.start({iv: iv});
decipher.update(forge.util.createBuffer(forge.util.decode64("[STORED_CIPHER_TEXT_HERE]")));
decipher.finish();
var decipheredText = decipher.output.toString();

If you include some kind of remote data storage in your application, now you can add peace of mind that the data will be protected.

The following is an example service you can use if you’re operating with AngularJS:

exampleApp.service("CipherService", function() {

    /*
     * Encrypt a message with a passphrase or password
     *
     * @param    string message
     * @param    string password
     * @return   object
     */
    this.encrypt = function(message, password) {
        var salt = forge.random.getBytesSync(128);
        var key = forge.pkcs5.pbkdf2(password, salt, 4, 16);
        var iv = forge.random.getBytesSync(16);
        var cipher = forge.cipher.createCipher('AES-CBC', key);
        cipher.start({iv: iv});
        cipher.update(forge.util.createBuffer(message));
        cipher.finish();
        var cipherText = forge.util.encode64(cipher.output.getBytes());
        return {cipher_text: cipherText, salt: forge.util.encode64(salt), iv: forge.util.encode64(iv)};
    }

    /*
     * Decrypt cipher text using a password or passphrase and a corresponding salt and iv
     *
     * @param    string (Base64) cipherText
     * @param    string password
     * @param    string (Base64) salt
     * @param    string (Base64) iv
     * @return   string
     */
    this.decrypt = function(cipherText, password, salt, iv) {
        var key = forge.pkcs5.pbkdf2(password, forge.util.decode64(salt), 4, 16);
        var decipher = forge.cipher.createDecipher('AES-CBC', key);
        decipher.start({iv: forge.util.decode64(iv)});
        decipher.update(forge.util.createBuffer(forge.util.decode64(cipherText)));
        decipher.finish();
        return decipher.output.toString();
    }

});

The best part of this, is you can use it in mobile hybrid applications. I for example, am using Forge with Ionic Framework for Android and iOS.

Nic Raboy

Nic Raboy

Nic Raboy is an advocate of modern web and mobile development technologies. He has experience in C#, JavaScript, Golang and a variety of frameworks such as Angular, NativeScript, and Unity. Nic writes about his development experiences related to making web and mobile development easier to understand.