2

I'm trying to port the Java code for AES ECB encryption into node.js

The issues is that the output from java and node are not the same.

Here's the java code

 public static final String DEFAULT_ENCODING = "UTF-8";
 public static final String SEC_PROVIDER = "SunJCE";
 public static final String AES_ALGORITHM = "AES";
 public static final String RIJNDAEL_CIPHER = **"Rijndael/ECB/NoPadding"**;
 public static final int **CIPHER_PAD_SIZE = 32**;
 public static final String HEX_KEY = "3b6ce332ca3b6519eac769710f41ca5c";

public static String encryptData(String text, String hexKey) throws 
    Exception {
     byte[] b1 = Hex.decodeHex(HEX_KEY.toCharArray());
     SecretKey key = new SecretKeySpec(b1, AES_ALGORITHM); 
     Cipher cipher = Cipher.getInstance(RIJNDAEL_CIPHER, SEC_PROVIDER); 
     text = padRightToMod(text, CIPHER_PAD_SIZE); 
     byte[] buf = text.getBytes(DEFAULT_ENCODING); 
     cipher.init(Cipher.ENCRYPT_MODE, key);
     buf = cipher.doFinal(buf); 
     String result = new String(Hex.encodeHex(buf)); 
     result = result.toUpperCase(); 
     return result;
}

// ensure block size of 32
public static String padRightToMod(String text, int mod) {
    if (text == null || mod <= 0) {
        return text;
    }
    final int len = text.length();
    StringBuilder buf = new StringBuilder(512);
    buf.append(text);
    for (int i = len; i % mod > 0; i++) {
        buf.append(" ");
    }
    String rs = buf.toString();
    System.out.println(rs.length());
    return rs;
}

// Call to the encrypt function 

   String encText = encryptData("Hello", HEX_KEY);

The result is CC0AC95B5FFD4758DBFA40F909C285F0F86A8F19ED1A12C1BFC098348A2AC683

And with this javascript code

crypto = require('crypto');

function encrypt(data,key) {
var cipher = crypto.createCipher('**aes-128-ecb**', key); //create aes cipher 
var encrypted = cipher.update(data,'utf8', 'hex'); //output as hex
return encrypted;
}

function padRightTo32(str) // ensure block size of 32
  {
    len=str.length;
    for(i=len; i%32>0; i++){
    str=str +" ";
  }
  return str;
 }

// call to encryption function
hexkey="3b6ce332ca3b6519eac769710f41ca5c"
encStr=encrypt(padRightTo32("Hello"),hexKey);
console.log(encStr);

The result is 1B928CF3C18D53BA5138DD1484D181939FD2B7BB2A17AE6A79664488B5C12652

==== Update ======

I tried https://github.com/Snack-X/rijndael-js implementaiton form github with this code

const Rijndael = require("./node_modules/node-rijndael-master");
function padRightTo32(str)
{
    len=str.length;

    for(i=len; i%32>0; i++){
        str=str +" ";
    }
    console.log(str);
    console.log(str.length);
return str;
}
let key = "3b6ce332ca3b6519eac769710f41ca5c";
let original = padRightTo32("Hello");
let cipher = new Rijndael(key, "ecb");
let ciphertext = cipher.encrypt(original, 128);
console.log(ciphertext.toString("hex"));

I get this result e97282fb5838a9c78e6df1f1b4aad108aa010418ec573d74b9c991f4e897e752 but not the encrypted text that Iget from in java. Trying the 256 block size doesn't help either.

what I'm missing that is resulting in a different output?

Silver.Rainbow
  • 425
  • 4
  • 14
  • 2
    Rijndael and AES are not the same. Your Java code goes to a lot of effort to ensure a 256-bit block size. AES has a 128-bit block size. You aren't using the same encryption algorithm. – Luke Joshua Park Feb 25 '19 at 20:57
  • 1
    Also note that the code you have is massively insecure... It uses ECB mode and isn't authenticated. Why are you using this at all? – Luke Joshua Park Feb 25 '19 at 20:57
  • @LukeJoshuaPark Thanks for taking the time. I thought Rijndeal was AES based on Wikipedia https://en.wikipedia.org/wiki/Advanced_Encryption_Standard. – Silver.Rainbow Feb 25 '19 at 21:17
  • The code is from a vendor we are integrating with and I need to perform encryption of some data in "Rijndael/ECB/NoPadding" mode. The communication will be with whitelisted IPs only. – Silver.Rainbow Feb 25 '19 at 21:19
  • Any suggestion how to approach Rijndae encryption in javascript? – Silver.Rainbow Feb 25 '19 at 21:29
  • 1
    There are a number of NodeJS packages for Rijndael. – Luke Joshua Park Feb 25 '19 at 21:34
  • @LukeJoshuaPark. Thanks. I tried Rijndael library from github bu that did not help either. I have added that to the original question. – Silver.Rainbow Feb 25 '19 at 22:19

1 Answers1

3

Concerning your key you have to convert your hex-string into binary data using a buffer (see e.g. Encrypt binary data with aes-ecb on node.js).

Moreover, you have to use the method crypto.createCipheriv to instantiate the cipher (see e.g. https://nodejs.org/api/crypto.html#crypto_crypto_createcipheriv_algorithm_key_iv_options). The currently used (deprecated) method crypto.creataCipher expects a password and generates the key from the password (see e.g. https://nodejs.org/api/crypto.html#crypto_crypto_createcipher_algorithm_password_options).

The following code

crypto = require('crypto');

function encrypt(data,key) {
    var cipher = crypto.createCipheriv('aes-128-ecb', key,''); //create aes-128 cipher 
    var encrypted = cipher.update(data,'utf8', 'hex'); //output as hex
    return encrypted;
}

function padRightTo32(str) { // ensure block size of 32

    len=str.length;
    for(i=len; i%32>0; i++) {
        str=str +" ";
    }
    return str;
}

// call to encryption function
var hexKey = new Buffer('3b6ce332ca3b6519eac769710f41ca5c', 'hex'); // 16 Byte-key
encStr=encrypt(padRightTo32("Hello"),hexKey);
console.log(encStr);  

has the output

cc0ac95b5ffd4758dbfa40f909c285f0f86a8f19ed1a12c1bfc098348a2ac683

which is equal to the output of the Java code.

In Java the length of the key defines the used AES-variant, e.g. if you choose a 16-Byte key AES-128 is used, if a 32-Byte key is chosen, AES-256 is used. In the nodejs-code you have to explicitly specifiy the AES-variant, i.e. aes-128-ecb for a 16-Byte key, and aes-256-ecb for a 32-Byte key etc.

As already mentioned in the comments ECB in't a secure mode (see e.g. https://crypto.stackexchange.com/questions/20941/why-shouldnt-i-use-ecb-encryption).

I don't know if there is really a difference between Rijndael/ECB/NoPadding and AES/ECB/NoPadding concerning the cipher-instantiation in Java. In my testcases at least the results are identical. Thus, for the nodejs-code the selection of aes-128-ecb (for 16-Byte key) or aes-256-ecb (for 32-Byte keys) should work.

Topaco
  • 40,594
  • 4
  • 35
  • 62
  • Ironic that they've gone to all this effort around 256-bit blocksizes and they just end up with 128-bit ones and some message bloat... This is why cryptography should be done by cryptographers. Good answer. – Luke Joshua Park Feb 25 '19 at 22:22
  • @Topaco. This is extremely helpful and I learned something new today. Thank you so much. – Silver.Rainbow Feb 25 '19 at 22:27
  • @LukeJoshuaPark Thanks to you too. I guess AES is Rijndael afterall. – Silver.Rainbow Feb 25 '19 at 22:28
  • @Narshal Rijndael **IS NOT** AES... Rijndael with a 128-bit block size is AES. Rijndael also comes in 192-bit and 256-bit block sizes. They are **not** the same. The developer that wrote the original code *thinks* they are using Rijndael with a 256-bit block size but they're actually just expanding their plaintexts to use multiples of 2 128-bit blocks, which is why this answer works. – Luke Joshua Park Feb 25 '19 at 22:29
  • 1
    @LukeJoshuaPark You are right. I confused key size with block size. When I set CIPHER_PAD_SIZE to 16, the encryption and decryption works. I'm not sure why this approach from the developer but I'm just evaluating a product so I'm unblocked and enlightened. Thank you :) – Silver.Rainbow Feb 25 '19 at 23:24