How to decrypt a cryptojs AES encrypted message at the java server side?
Asked Answered
C

3

11

I have the following cryptojs based javascript encryption/decryption functions which works perfectly fine.

I use a random salt, random iv value and a specific password while encrypting the message using cryptpjs. I reuse the same salt, iv and the password to generate the key while decrypting the encrypted message.

This part works well..

function  encrypt(){
  var salt = CryptoJS.lib.WordArray.random(128/8);
  var iv = CryptoJS.lib.WordArray.random(128/8);
  console.log('salt  '+ salt );
  console.log('iv  '+ iv );
  var key128Bits = CryptoJS.PBKDF2("Secret Passphrase", salt, { keySize: 128/32 }); 
  console.log( 'key128Bits '+ key128Bits);
  var key128Bits100Iterations = CryptoJS.PBKDF2("Secret Passphrase", salt, { keySize: 128/32, iterations: 100 });
  console.log( 'key128Bits100Iterations '+ key128Bits100Iterations);
  var encrypted = CryptoJS.AES.encrypt("Message", key128Bits100Iterations, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7  });
  console.log('encrypted   '+ encrypted  );
}

function  decrypt(){
  var salt = CryptoJS.enc.Hex.parse("4acfedc7dc72a9003a0dd721d7642bde");
  var iv = CryptoJS.enc.Hex.parse("69135769514102d0eded589ff874cacd");
  var encrypted = "PU7jfTmkyvD71ZtISKFcUQ==";
  console.log('salt  '+ salt );
  console.log('iv  '+ iv );
  var key = CryptoJS.PBKDF2("Secret Passphrase", salt, { keySize: 128/32, iterations: 100 });
  console.log( 'key '+ key);
  var decrypt = CryptoJS.AES.decrypt(encrypted, key, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 });
  var ddd = decrypt.toString(CryptoJS.enc.Utf8); 
  console.log('ddd '+ddd);
} 

But the issue starts when I try to decrypt the same encrypted text at the java server side. I want the encrypted message to be decrypted by my java server code. Here is the Java code that I wrote:

public static void main(String args[]) throws Exception{
  String password = "Secret Passphrase";
  String salt = "4acfedc7dc72a9003a0dd721d7642bde";
  String iv = "69135769514102d0eded589ff874cacd";
  String encrypted = "PU7jfTmkyvD71ZtISKFcUQ==";
  byte[] saltBytes = salt.getBytes(); //hexStringToByteArray(salt);
  byte[] ivBytes = iv.getBytes();//hexStringToByteArray(iv);
  IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);        
  SecretKeySpec sKey = (SecretKeySpec) generateKeyFromPassword(password, saltBytes);
  System.out.println( decrypt( encrypted , sKey ,ivParameterSpec));
}

public static SecretKey generateKeyFromPassword(String password, byte[] saltBytes) throws GeneralSecurityException {

  KeySpec keySpec = new PBEKeySpec(password.toCharArray(), saltBytes, 100, 128/32);
  SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
  SecretKey secretKey = keyFactory.generateSecret(keySpec);
  return new SecretKeySpec(secretKey.getEncoded(), "AES");
}

public static String decrypt(String encryptedData, SecretKeySpec sKey, IvParameterSpec ivParameterSpec) throws Exception {

  Cipher c = Cipher.getInstance("AES");
  c.init(Cipher.DECRYPT_MODE, sKey, ivParameterSpec);
  byte[] decordedValue = new BASE64Decoder().decodeBuffer(encryptedData);
  byte[] decValue = c.doFinal(decordedValue);
  String decryptedValue = new String(decValue);
  return decryptedValue;
}

But I get the following exception:

Exception breakpoint: SecretKeySpec.java:96, java.lang.IllegalArgumentException, Empty key
Exception in thread "main" java.lang.IllegalArgumentException: Empty key
at javax.crypto.spec.SecretKeySpec.<init>(SecretKeySpec.java:96)

I have no idea what I should do

Congregational answered 2/12, 2014 at 11:46 Comment(0)
G
10

This part of your code is wrong:

KeySpec keySpec = new PBEKeySpec(password.toCharArray(), saltBytes, 100, 128/32);
//->---------------------------------------------------------------------^^^^^^^

The 128/32 value is erroneous. You need either 128, 192 or 256. Currently you have the equivalent of 4, which seems to result in no output at all from the PBKDF2 function.

Also, in Java you should use DatatypeConverter.parseHexBinary(), or similar, to convert hex into bytes. Currently you are just calling getBytes() which isn't right.

Finally, you need to specify CBC mode and PKCS#5 padding in order to match your Javascript code. So change the line to:

Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
Garofalo answered 2/12, 2014 at 13:4 Comment(1)
The moment i did the changes you suggested , it worked . Thanks Duncan. I will provide the complete working solution as another answer. Thanks heaps. You were spot on and fixed my issues.Congregational
C
9

Thanks to Duncan for the prompt response and advice. I am giving the complete solution that worked for me below for the benefit of others.

Java code to do the decryption of the cryptojs encrypted message

public static void main(String args[]) throws Exception{

 String password = "Secret Passphrase";
 String salt = "222f51f42e744981cf7ce4240eeffc3a";
 String iv = "2b69947b95f3a4bb422d1475b7dc90ea";
 String encrypted = "CQVXTPM2ecOuZk+9Oy7OyGJ1M6d9rW2D/00Bzn9lkkehNra65nRZUkiCgA3qlpzL";

 byte[] saltBytes = hexStringToByteArray(salt);
 byte[] ivBytes = hexStringToByteArray(iv);
 IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);        
 SecretKeySpec sKey = (SecretKeySpec) generateKeyFromPassword(password, saltBytes);
 System.out.println( decrypt( encrypted , sKey ,ivParameterSpec));

}

public static SecretKey generateKeyFromPassword(String password, byte[] saltBytes) throws GeneralSecurityException {

 KeySpec keySpec = new PBEKeySpec(password.toCharArray(), saltBytes, 100, 128);
 SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
 SecretKey secretKey = keyFactory.generateSecret(keySpec);

 return new SecretKeySpec(secretKey.getEncoded(), "AES");
}

public static byte[] hexStringToByteArray(String s) {

 int len = s.length();
 byte[] data = new byte[len / 2];

 for (int i = 0; i < len; i += 2) {
    data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
    + Character.digit(s.charAt(i+1), 16));
 }

  return data;

}

public static String decrypt(String encryptedData, SecretKeySpec sKey, IvParameterSpec ivParameterSpec) throws Exception { 

 Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding");
 c.init(Cipher.DECRYPT_MODE, sKey, ivParameterSpec);
 byte[] decordedValue = new BASE64Decoder().decodeBuffer(encryptedData);
 byte[] decValue = c.doFinal(decordedValue);
 String decryptedValue = new String(decValue);

 return decryptedValue;
}
Congregational answered 2/12, 2014 at 23:19 Comment(3)
I've used your Javascript code (in question) and Java code (in this answer) as example and I've implemented it. Java code is successfully working when the password, salt, iv and encrypted are hardcoded like done in this answer. I also used your javascript code for client side, but it seems like there is some misconfigurations in the js side like key size and so on, coz of that I'm getting bad padding exception in java when decoding the javascript encoded data. Can you provide the working javascript code..Millimicron
It's "Given Final block not properly padded" error in javaMillimicron
May i ask how can I implement this into Android Studio java? Because i got the error message unable to resolve "Base64Decoder" and i found that android studio doesnt support sun.misc package.Arable
A
0

Above Solution didnt worked for me, With small modification it worked

Backend Java Code

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.security.spec.KeySpec;
import java.util.Base64;


public class AESCBCPasswordEncryptor {

    private static final String ENCRYPT_ALGO = "AES/CBC/PKCS5Padding";
    private static final String SECRET_KEY = "YourKey";
    private static final String SALT = "YourKey";
    private static final String IV = "YourKey";

    public static String decrypt(String strToDecrypt) {
        try {

            byte[] ivBytes = DatatypeConverter.parseHexBinary(IV);
            // byte[] iv = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
            IvParameterSpec ivspec = new IvParameterSpec(ivBytes);

            byte[] saltBytes = DatatypeConverter.parseHexBinary(SALT);
            KeySpec spec = new PBEKeySpec(SECRET_KEY.toCharArray(), saltBytes, 100, 128);

            SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
            SecretKey secretKey = factory.generateSecret(spec);
            SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getEncoded(), "AES");

            Cipher cipher = Cipher.getInstance(ENCRYPT_ALGO);
            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivspec);
            return new String(cipher.doFinal(Base64.getDecoder().decode(strToDecrypt)));
        } catch (Exception e) {
            System.out.println("Error while decrypting: " + e.toString());
        }
        return null;
    }

    public static void main(String[] args) throws Exception {
        String decryptedText = decrypt("BMbmxr3ikg33xHHf0gwweogBQcGWMKyCYo1ACO+Sb/Q=");
        System.out.println( "Decrypted plain text : "+ decryptedText);
    }

}

Java Script Code

function encrypt() {
  // var salt = CryptoJS.lib.WordArray.random(128/8);
  // var iv = CryptoJS.lib.WordArray.random(128/8);
  var salt = CryptoJS.enc.Hex.parse("YourKey");
  var iv = CryptoJS.enc.Hex.parse("YourKey");
  console.log("salt  " + salt);
  console.log("iv  " + iv);

  var key128Bits100Iterations = CryptoJS.PBKDF2(
    "YourKey",
    salt,
    {
      keySize: 128 / 32,
      iterations: 100
    }
  );
  console.log("key128Bits100Iterations " + key128Bits100Iterations);
  var encrypted = CryptoJS.AES.encrypt(
    "Hello My Friend2",
    key128Bits100Iterations,
    { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }
  );
  console.log("encrypted   " + encrypted);
}
Alabaster answered 12/10, 2022 at 9:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.