简体   繁体   中英

Encryption interop between Java (Cipher) & JavaScript (crypto-js)

I've been tasked to fix an interoping encryption algorithm that was working perfectly fine from before but suddenly went haywire for reasons unknown, no one has touched any of the code for both of the languages (Java & JS).

I'm not really well verse with cryptography so I don't know what possible solutions to look for or work with. The task was basically to have this encryption code on Java translated to JavaScript that would both have a resulting Base64 string that was to be decrypted through Java.

The following are the code snippets for the encryption being done with Java & JS and decryption process on Java:

Java Encryption

public static String encryptMsg(String message) {
    @SuppressLint("GetInstance") Cipher cipher = null;
    try {
        cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, secret);
        byte[] cipherText = cipher.doFinal(message.getBytes(UTF_CHARSET));
        return Base64.encodeToString(cipherText, Base64.DEFAULT);
    } catch (NoSuchAlgorithmException | NoSuchPaddingException | BadPaddingException | IllegalBlockSizeException | InvalidKeyException e) {
        e.printStackTrace();
    } catch (NullPointerException e) {
        //Do nothing, nothing to encrypt
    }
    return null;
}

JavaScript Encryption

function encryptData(data, key) {
 const options = {
  mode: Crypto.mode.ECB,
  padding: Crypto.pad.Pkcs7
 }
 const secret = Crypto.enc.Utf8.parse(key)
 const encrypted = Crypto.AES.encrypt(data, secret, options)
 return encrypted.ciphertext.toString(Crypto.enc.Base64)
}

Java Decryption

public static String decryptMsg(String base64cipherText) {
    @SuppressLint("GetInstance") Cipher cipher = null;
    try {
        cipher = Cipher.getInstance(TRANSFORMATION);
        cipher.init(Cipher.DECRYPT_MODE, secret);
        String decryptString = new String(cipher.doFinal(Base64.decode(base64cipherText, Base64.DEFAULT)), UTF_CHARSET);
        return decryptString;
    } catch (NoSuchAlgorithmException | NoSuchPaddingException | BadPaddingException | IllegalBlockSizeException | InvalidKeyException e) {
        e.printStackTrace();
    } catch (NullPointerException e) {
        //Do nothing, nothing to decrypt
    }
    return null;
}

Currently the results return null on the encrypted string using the JavaScript encryption function when being decrypted so it's probably encrypting correctly (?) I'm not sure what I'm missing or doing wrong here...

Seems you are missing IV (initialization vector).

don't really know what an IV is or if it's needed here, the encryption Java code doesn't state it anywhere

The IV is an initialization vector allowing to reuse a key to encrypt multiple messages (or blocks), please have a look at the CBC block mode as you are using it.

I am not sure for JavaScript API, but at least I can give you example in Java. As well you can have a look at my blog about crypto examples

Java Encryption

 SecureRandom rnd = new SecureRandom();
 byte[] iv = new byte[SYMMETRIC_BLOCK_SIZE / 8];
 IvParameterSpec ivParamSpec = new IvParameterSpec(iv);
 SecretKey symmetricKey = new SecretKeySpec(encryptionParams.getKey(), SYMMETRIC_KEY_ALG);

Cipher cipher = Cipher.getInstance(SYMMETRIC_CIPHER_NAME);
cipher.init(Cipher.ENCRYPT_MODE, symmetricKey, ivParamSpec);

byte[] encrypted = cipher.doFinal(encryptionParams.getPlaintext());
/* and encoded form can contain form of base64( IV + ciphertext )  */

For CBC mode the IV must be random. If you don't specify the IVParameter, it will be generated and you can read it from cipher.getIV(); . The IV can be public, it is usually prepended before the ciphertext, as the IV is needed to decrypt the ciphertext itself.

Java Decryption

/* if IV is prepended before the ciphertext, it can be fetched as sub-array
  of the decoded message */
IvParameterSpec ivParamSpec = new IvParameterSpec(iv);

Cipher cipher = Cipher.getInstance(SYMMETRIC_CIPHER_NAME);
cipher.init(Cipher.DECRYPT_MODE, symmetricKey, ivParamSpec);

byte[] decrypted = cipher.doFinal(encryptionParams.getCiphertext());

In this example there's no Mac (message authentication code included), you can have a look at the linked example.

For JavaScript you should have a look at the used API, but the principle stays the same (you have to generate, use, pass and provide the IV too somehow). This blog seems to contain be more complete code.

var iv = CryptoJS.lib.WordArray.random(128/8);
var encrypted = CryptoJS.AES.encrypt(msg, key, { 
  iv: iv, 
  padding: CryptoJS.pad.Pkcs7,
  mode: CryptoJS.mode.CBC
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM