0

I'm trying to replicate the encryption used in a Java application using Javascript and CryptoJS. I'm not quite sure how I should be replicating the SecretKeySpec, as it seems CryptoJS expects a string for the key.

Here is the Java encryption code that I need to replicate in JS:

public byte[] encrypt(byte[] origin)
    {
        String key = "testkey";
        SecretKeySpec sks = new SecretKeySpec(convertAESKey(key), "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        byte[] iv = new byte[16];
        cipher.init(Cipher.ENCRYPT_MODE, sks, new IvParameterSpec(iv));
        return cipher.doFinal(origin);
    }

private byte[] convertAESKey(String key)
   {
        byte[] keyBytes;
        keyBytes = key.getBytes("UTF-8");
        byte[] keyBytes16 = new byte[16];
        System.arraycopy(keyBytes, 0, keyBytes16, 0,
                Math.min(keyBytes.length, 16));
        return keyBytes16;
    }
}

My JS code so far:

var iv = new Uint8Array(16);
var key = "testkey";

var encrypted = CryptoJS.AES.encrypt(
    someByteArray,
    key,
    {
        iv: iv,
        mode: CryptoJS.mode.CBC
    }
);

Also, the final output of cipher is a encrypted byte array. The final output of CryptoJS appears to be an object with cipherText. Is there a way to get the output as a byte array ?

The only thing I could think of now is to convert the encrypted response into a string, then converting that into a byte array, but this doesn't seem to match the output of the Java encryption.

1
  • Using the encryption out of the box is working for me no problem. The issue is replicating the Java encryption as the current implementation is Client(Java) to Server(Java), and I need to make it Client(Javascript) to Server(Java). The server code cannot change, so it is important to be able to match the encryption exactly. Commented Aug 5, 2016 at 21:21

1 Answer 1

1

CryptoJS doesn't handle Uint8Array by default, so I'm not going to use it and instead will use the default WordArray for representing binary data.

var iv = CryptoJS.lib.WordArray.create([0, 0, 0, 0]);
var key = "testkey";

key = CryptoJS.enc.Utf8.parse(key);
for (var i = key.words.length; i < 4; i++) {
    key.words[i] = 0;
}
key.sigBytes = 16;
key.clamp();

var encrypted = CryptoJS.AES.encrypt(
    someByteArray,
    key,
    {
        iv: iv,
        mode: CryptoJS.mode.CBC
    }
);
console.log(encrypted.ciphertext.toString(CryptoJS.enc.Hex));

This will give a hex string which can be easily compared to the ciphertext produced by Java.


This code is not very secure:

  • The IV must be unpredictable (read: random). Don't use a static IV, because that makes the cipher deterministic and therefore not semantically secure. An attacker who observes ciphertexts can determine when the same message prefix was sent before. The IV is not secret, so you can send it along with the ciphertext. Usually, it is simply prepended to the ciphertext and sliced off before decryption.

  • The key must be random (read: look like random noise). Text doesn't look random and you would severely reduce your security by (mis-)using a password as a key. If you have a password, then you need to derive a key from that using something like PBKDF2, bcrypt, scrypt or Argon2 with a high iteration count / cost factor.

  • You should authenticate your ciphertexts in order to detect (malicious) manipulation. If you don't do this, then this might be vulnerable against a padding oracle attack. Authentication can be accomplished with an authenticated mode of operation like GCM or EAX, or using an encrypt-then-MAC scheme with a strong MAC such as HMAC-SHA256.

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks for this, i'm trying to use this and I get "undefined is not an object (evaluating 'e.length')"
You haven't said what someByteArray is, so I assumed that it either is a string or a WordArray. If it is a Uint8Array, you can include the lib-typedarrays.js file to get the support.
I've tried including the lib-typedarrays.js file and still receive the same error.. the array im trying to encode is Uint8Array[120, 156, 203, 72, 205, 201, 201, 7, 0, 6, 44, 2, 21]
The array that you show here is too short to be a valid ciphertext. Anyway, you may need to convert it yourself from Uint8Array to WordArray. Include lib-typedarrays.js and then convert it with someByteArray = CryptoJS.lib.WordArray.create(someByteArray);

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.