Core Java

AES Encryption in Javascript and Decryption in Java

AES stands for Advanced Encryption System and it’s a symmetric encryption algorithm.Many times we require to encrypt some plain-text such as password at the client side and send it to server and then server decrypts it to process further.AES encryption and decryption is easier to implement in the same platform such as Android client and Java server but sometimes it becomes challenging to decrypt an AES encrypted password in cross platform environment such as Javascript client and Java Server such as in spring mvc framework because if any system defaults do not match then the decryption will fail.

In this article we will be creating an application with spring mvc and angular js client. We will have a login page with form inputs for userrname and password. Before sending the password to the server, the password will be encrypted in javascript using CryptoJS and the same encrypted password will be decrypted in java and a comparison will be done to match the password.We will be generating salt and IV in javascript and then generate the key using PBKDF2 function from the passphrase, salt and key size.After this we will be encrypting the plaintext using key and IV and the same will be decrypted in Java.So basically we will be developing a mechanism for interoperable AES encryption with Java and Javascript.

Before proceeing further, let us make one thing clear that this mechanism only adds a kind of extra security during wire transmission of data(most probably) but does not provide full proof security. If you’re not using SSL, the attacker can just perform a man-in-the-middle attack and steal the data by giving the user a different key.

Project Structure

We have a spring boot and angular Js web app setup.Following is the structure.

Aes Encryption in javascript

For AES encryption in javascript we have imported two js files – crypto.js and pbkdf2.js.We have AesUtil.js that has common codes to perform encryption and decryption. Here this.keySize is the size of the key in 4-byte blocks.Hence, to use a 128-bit key, we have divided the number of bits by 32 to get the key size used for CryptoJS.

AesUtil.js

var AesUtil = function(keySize, iterationCount) {
  this.keySize = keySize / 32;
  this.iterationCount = iterationCount;
};

AesUtil.prototype.generateKey = function(salt, passPhrase) {
  var key = CryptoJS.PBKDF2(
      passPhrase, 
      CryptoJS.enc.Hex.parse(salt),
      { keySize: this.keySize, iterations: this.iterationCount });
  return key;
}

AesUtil.prototype.encrypt = function(salt, iv, passPhrase, plainText) {
  var key = this.generateKey(salt, passPhrase);
  var encrypted = CryptoJS.AES.encrypt(
      plainText,
      key,
      { iv: CryptoJS.enc.Hex.parse(iv) });
  return encrypted.ciphertext.toString(CryptoJS.enc.Base64);
}

AesUtil.prototype.decrypt = function(salt, iv, passPhrase, cipherText) {
  var key = this.generateKey(salt, passPhrase);
  var cipherParams = CryptoJS.lib.CipherParams.create({
    ciphertext: CryptoJS.enc.Base64.parse(cipherText)
  });
  var decrypted = CryptoJS.AES.decrypt(
      cipherParams,
      key,
      { iv: CryptoJS.enc.Hex.parse(iv) });
  return decrypted.toString(CryptoJS.enc.Utf8);
}

Password Encryption in javascript

The method logMeIn() will be called after the click of submit button. This method will use the common code defined in AesUtil.js to encrypt the password and make POST request to validate the password.The password sent will be in the form iv::salt::ciphertext In the server side, java will decrypt the password and send the decrypted password in the response which will be shown in the alert box.

var app = angular.module('demoApp', []);
app.controller('loginController', ['$scope', '$rootScope', '$http', function ($scope, $rootScope, $http) {

    $scope.logMeIn = function(){
        if(!$scope.userName || !$scope.password){
            $scope.showMessage("Missing required fields.", false);
            return;
        }
        var iv = CryptoJS.lib.WordArray.random(128/8).toString(CryptoJS.enc.Hex);
        var salt = CryptoJS.lib.WordArray.random(128/8).toString(CryptoJS.enc.Hex);

        var aesUtil = new AesUtil(128, 1000);
        var ciphertext = aesUtil.encrypt(salt, iv, $('#key').text(), $scope.password);

        var aesPassword = (iv + "::" + salt + "::" + ciphertext);
        var password = btoa(aesPassword);
        var data = {
            userName: $scope.userName,
            password: password
        }

        $http.post('/login',data).then(function (response){
			if(response.status === 200){
                alert("Password is " + response.data.password);
			}else {
			    alert("Error occurred");
            }
		})
	};

	}]);

AES Decryption in Java

First let us implement the controller class that will intercept the login request. Here we have hardcoded the key.This key will be uniquely generated by the server and sent to client for each login request. The client will be using the same key while encryption and server will use the same key for decryption.Make sure the key length is 16 because we are using 128 bit encryption. Remember the format of encrypted text that we are sending from the client side – iv::salt::ciphertext. The text is decrypted in the same format. We already have IV, salt and cipher text.

package com.example.demo.controller;

import com.example.demo.model.Credentials;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

@Controller
public class WelcomeController {

	private static final Logger LOGGER = LoggerFactory.getLogger(WelcomeController.class);

	@RequestMapping(value={"/login"},method = RequestMethod.GET)
    public String loginPage(HttpServletRequest request){
		LOGGER.info("Received request for login page with id - " + request.getSession().getId());
		String randomKey = UUID.randomUUID().toString();
		//String uniqueKey = randomKey.substring(randomKey.length()-17, randomKey.length() -1);
        String uniqueKey = "1234567891234567";
	    request.getSession().setAttribute("key", uniqueKey);
	    return "index";
    }

    @RequestMapping(value={"/login"},method = RequestMethod.POST)
    public @ResponseBody ResponseEntity login(@RequestBody Credentials credentials, HttpServletRequest request) {
        String decryptedPassword =  new String(java.util.Base64.getDecoder().decode(credentials.getPassword()));
        AesUtil aesUtil = new AesUtil(128, 1000);
        Map map = new HashMap<>();
        if (decryptedPassword != null && decryptedPassword.split("::").length == 3) {
            LOGGER.info("Password decrypted successfully for username - " + credentials.getUserName());
            String password = aesUtil.decrypt(decryptedPassword.split("::")[1], decryptedPassword.split("::")[0], "1234567891234567", decryptedPassword.split("::")[2]);
            map.put("password", password);
        }
        return new ResponseEntity<>(map, HttpStatus.OK);
    }

}

Following is the java util class for AES encryption and decryption.You can follow AES encryption and decryption in java for more detailed explanation regarding following implementation.

AesUtil.java
package com.example.demo.controller;

import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;

import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;

public class AesUtil {
    private final int keySize;
    private final int iterationCount;
    private final Cipher cipher;
    
    public AesUtil(int keySize, int iterationCount) {
        this.keySize = keySize;
        this.iterationCount = iterationCount;
        try {
            cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        }
        catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
            throw fail(e);
        }
    }
    
    public String decrypt(String salt, String iv, String passphrase, String ciphertext) {
        try {
            SecretKey key = generateKey(salt, passphrase);
            byte[] decrypted = doFinal(Cipher.DECRYPT_MODE, key, iv, base64(ciphertext));
            return new String(decrypted, "UTF-8");
        }
        catch (UnsupportedEncodingException e) {
            return null;
        }catch (Exception e){
            return null;
        }
    }
    
    private byte[] doFinal(int encryptMode, SecretKey key, String iv, byte[] bytes) {
        try {
            cipher.init(encryptMode, key, new IvParameterSpec(hex(iv)));
            return cipher.doFinal(bytes);
        }
        catch (InvalidKeyException
                | InvalidAlgorithmParameterException
                | IllegalBlockSizeException
                | BadPaddingException e) {
            return null;
        }
    }
    
    private SecretKey generateKey(String salt, String passphrase) {
        try {
            SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
            KeySpec spec = new PBEKeySpec(passphrase.toCharArray(), hex(salt), iterationCount, keySize);
            SecretKey key = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
            return key;
        }
        catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
            return null;
        }
    }

    public static byte[] base64(String str) {
        return Base64.decodeBase64(str);
    }
    
    public static byte[] hex(String str) {
        try {
            return Hex.decodeHex(str.toCharArray());
        }
        catch (DecoderException e) {
            throw new IllegalStateException(e);
        }
    }
    
    private IllegalStateException fail(Exception e) {
        return null;
    }

}

Testing AES Encryption and Decryption

Run DemoApplication.java as a java application and hit http://localhost:8080. Once the login page appears you can enter the username and password and click submit button and you can see the decrypted password in the alert.


/>

Conclusion

In this post we discussed about interoperable AES encryption with Java and Javascript. We used Crypto.js library to perform this encryption in javascript. The complete source ode can be found here.If you have anything that you want to add or share then please share it below in the comment section

Published on Java Code Geeks with permission by Dhiraj Ray, partner at our JCG program. See the original article here: AES Encryption in Javascript and Decryption in Java

Opinions expressed by Java Code Geeks contributors are their own.

Dhiraj Ray

He is a technology savvy professional with an exceptional capacity to analyze, solve problems and multi-task. He is an avid reader and a technology enthusiast who likes to be up to date with all the latest advancements happening in the techno world. He also runs his own blog @ devglan.com
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Mahesh
Mahesh
4 years ago

crypto.js file is not there in repository.

Back to top button