AES-256-CTR Encryption in node JS and decryption in Java

Ashish Pandey :

I am trying to encode in nodejs and decryption for the same in nodejs works well. But when I try to do the decryption in Java using the same IV and secret, it doesn't behave as expected.

Here is the code snippet:

Encryption in nodeJs:

var crypto = require('crypto'),
algorithm = 'aes-256-ctr',
_ = require('lodash');

var secret = 'd6F3231q7d19428743234@123nab@234';

function encrypt(text, secret) {
    var iv = crypto.randomBytes(16);
    console.log(iv);
    var cipher = crypto.createCipheriv(algorithm, new Buffer(secret), iv);
    var encrypted = cipher.update(text);

    encrypted = Buffer.concat([encrypted, cipher.final()]);

    return iv.toString('hex') + ':' + encrypted.toString('hex');
}
var encrypted = encrypt("8123497494", secret);
console.log(encrypted);

And the output is:

<Buffer 94 fa a4 f4 a1 3c bf f6 d7 90 18 3f 3b db 3f b9>
94faa4f4a13cbff6d790183f3bdb3fb9:fae8b07a135e084eb91e

Code Snippet for decryption in JAVA:

public class Test {

    public static void main(String[] args) throws Exception {
        String s = "94faa4f4a13cbff6d790183f3bdb3fb9:fae8b07a135e084eb91e";
        String seed = "d6F3231q7d19428743234@123nab@234";

        decrypt(s, seed);
    }

    private static void decrypt(String s, String seed)
            throws NoSuchAlgorithmException, NoSuchPaddingException, UnsupportedEncodingException, InvalidKeyException,
            InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
        String parts[] = s.split(":");
        String ivString = parts[0];
        String encodedString = parts[1];
        Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");

        byte[] secretBytes = seed.getBytes("UTF-8");

        IvParameterSpec ivSpec = new IvParameterSpec(hexStringToByteArray(ivString));

        /*Removed after the accepted answer
        MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] thedigest = md.digest(secretBytes);*/ 

        SecretKeySpec skey = new SecretKeySpec(thedigest, "AES");

        cipher.init(Cipher.DECRYPT_MODE, skey, ivSpec);
        byte[] output = cipher.doFinal(hexStringToByteArray(encodedString));

        System.out.println(new String(output));
    }
}

Output: �s˸8ƍ�

I am getting some junk value in the response. Tried a lot of options, but none of them seem to be working. Any lead/help is appreciated.

Ilmari Karonen :

In your JS code, you're using the 32-character string d6F3231q7d19428743234@123nab@234 directly as the AES key, with each ASCII character directly mapped to a single key byte.

In the Java code, you're instead first hashing the same string with MD5, and then using the MD5 output as the AES key. It's no wonder that they won't match.

What you probably should be doing, in both cases, is either:

  1. randomly generating a string of 32 bytes (most of which won't be printable ASCII characters) and using it as the key; or
  2. using a key derivation function (KDF) to take an arbitrary input string and turn it into a pseudorandom AES key.

In the latter case, if the input string is likely to have less than 256 bits of entropy (e.g. if it's a user-chosen password, most of which only have a few dozen bits of entropy at best), then you should make sure to use a KDF that implements key stretching to slow down brute force guessing attacks.


Ps. To address the comments below, MD5 outputs a 16-byte digest, which will yield an AES-128 key when used as an AES SecretKeySpec. To use AES-256 in Java, you will need to provide a 32-byte key. If trying to use a 32-byte AES key in Java throws an InvalidKeyException, you are probably using an old version of Java with a limited crypto policy that does not allow encryption keys longer than 128 bits. As described this answer to the linked question, you will either need to upgrade to Java 8 update 161 or later, or obtain and install an unlimited crypto policy file for your Java version.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=457012&siteId=1