Search code examples
javasecurityencryptioncryptographycryptojs

How to encrypt a message at client side using crypto-js library and decrypt it at the Java server


Background: the application that I am working on is supposed to work offline. I have an HTML5 page and the data keyed in by the user is encrypted using crypto-js library. And I want the encrypted message sent to java webserver and then decrypt it at the server side.

What am doing I am able to encrypt the message using Crypto-js

<code>
var message = "my message text";
var password = "user password";
var encrypted = CryptoJS.AES.encrypt( message ,password );
console.log(encrypted.toString());
// this prints an encrypted text "D0GBMGzxKXU757RKI8hDuQ=="
</code>

What I would like to do is pass the encrypted text "D0GBMGzxKXU757RKI8hDuQ== " to a java server side code and get the necrypted message decrypted.

I tried many options to decrypt the crypto-js encrypted message at the java server side. Please find below my code at the server side that is supposed to do the decryption of the encrypted text.

<code>
public static String decrypt(String keyText,String encryptedText) 
{
// generate key 
Key key = new SecretKeySpec(keyText.getBytes(), "AES");
Cipher chiper = Cipher.getInstance("AES");
chiper.init(Cipher.DECRYPT_MODE, key);
byte[] decordedValue = new BASE64Decoder().decodeBuffer(encryptedText);
byte[] decValue = chiper.doFinal(decordedValue);
String decryptedValue = new String(decValue);
return decryptedValue;
}  
</code>

I call the java method decrypt from below code

<code>
// performs decryption 
public static void main(String[] args) throws Exception 
{
String decryptedText = CrypterUtil.decrypt("user password","D0GBMGzxKXU757RKI8hDuQ==");
}
</code>

But i get the following exception when i run the java decrypt code

<code>
Exception in thread "main" java.security.InvalidKeyException: Invalid AES key length: 13 bytes
at com.sun.crypto.provider.AESCipher.engineGetKeySize(AESCipher.java:372)
at javax.crypto.Cipher.passCryptoPermCheck(Cipher.java:1052)
at javax.crypto.Cipher.checkCryptoPerm(Cipher.java:1010)
at javax.crypto.Cipher.implInit(Cipher.java:786)
at javax.crypto.Cipher.chooseProvider(Cipher.java:849)
at javax.crypto.Cipher.init(Cipher.java:1213)
at javax.crypto.Cipher.init(Cipher.java:1153)
at au.gov.daff.pems.model.utils.CrypterUtil.decrypt(CrypterUtil.java:34)
at au.gov.daff.pems.model.utils.CrypterUtil.main(CrypterUtil.java:47)
Process exited with exit code 1.
</code>

Am not sure what am I doing wrong ?... What is the best way to encrypt a message using the crypto-js library so that it can be decripted else where using user keyed in password.


Solution

  • Thanks to Artjom B and Isaac Potoczny-Jones 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 at the Java server side

    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;
    }
    

    The cryptojs javascript code that can do the encryption and decryption at the client side

    function  generateKey(){
        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 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  });
    }
    
    function  decrypt(){
        var salt = CryptoJS.enc.Hex.parse("4acfedc7dc72a9003a0dd721d7642bde");
        var iv = CryptoJS.enc.Hex.parse("69135769514102d0eded589ff874cacd");
        var encrypted = "PU7jfTmkyvD71ZtISKFcUQ==";
        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);
    }