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

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.


In the Java code you are taking the MD5 hash of secret before using it as a key:

MessageDigest md = MessageDigest.getInstance("MD5");
byte[] thedigest = md.digest(secretBytes);
SecretKeySpec skey = new SecretKeySpec(thedigest, "AES");

Whereas, in your NodeJS code, you don't do this anywhere. So you're using two different keys when encrypting and decrypting.

Don't copy and paste code without understanding it. Especially crypto code.