I am doing encryption in Java and decrypting it with JavaScript using AES-256/CBC, but actually this is the scenario: while encryption of .pdf, .jpg, in other words, other than text files Java produces the encrypted file with roughly the same bytes but in case of JavaScript(CryptoJS) it outputs ~1.5 times of the original file size, which is why I think for the decryption with CryptoJS it does not expect the small encrypted file size from Java. I have given the code from both sides i.e, encryption at Java side, decryption at JavaScript side.
Problem:
I have a requirement of encrypting any file with Java and decrypting the same file with JavaScript. Kindly refer to the code as given below and this example.
Java: Encryption
key generation:
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); KeySpec spec = new PBEKeySpec(passphrase.toCharArray(), hex(salt), iterationCount, keySize); SecretKey key = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
Encryption:
params:
private static final String IV = "F27D5C9927726BCEFE7510B1BDD3D137"; private static final String SALT = "3FF2EC019C627B945225DEBAD71A01B6985FE84C95A70EB132882F88C0A59A55"; private static final int KEY_SIZE = 128; private static final int ITERATION_COUNT = 10000; private static final String PASSWORD = "SET_YOUR_PASSWORD_HERE";
code:
public String encrypt(String salt, String iv, String passphrase, String plaintext) { try { SecretKey key = generateKey(salt, passphrase); byte[] encrypted = doFinal(Cipher.ENCRYPT_MODE, key, iv, plaintext.getBytes("UTF-8")); return base64(encrypted); } catch (Exception e) { throw fail(e); } } 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) { throw fail(e); } } public static String base64(byte[] bytes) { return Base64.getEncoder().encodeToString(bytes); } public static byte[] base64(String str) { return Base64.getDecoder().decode(str); } protected static byte[] hex(String str) { try { return decodeHex(str.toCharArray()); } catch (Exception e) { e.printStackTrace(); } return new byte[0]; } public static byte[] decodeHex(char[] data) throws Exception { int len = data.length; if ((len & 1) != 0) { throw new Exception("Odd number of characters."); } else { byte[] out = new byte[len >> 1]; int i = 0; for (int j = 0; j < len; ++i) { int f = toDigit(data[j], j) << 4; ++j; f |= toDigit(data[j], j); ++j; out[i] = (byte)(f & 255); } return out; } } protected static int toDigit(char ch, int index) throws Exception { int digit = Character.digit(ch, 16); if (digit == -1) { throw new Exception("Illegal hexadecimal character " + ch + " at index " + index); } else { return digit; } } 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; } private IllegalStateException fail(Exception e) { return new IllegalStateException(e); }
Decryption
params: (same as in above java code)
code:
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) }); var plaintext = decrypted.toString(CryptoJS.enc.Utf8);