3

I have used AES encryption in java as below and trying to decrypt in javascript

JAVA :

    byte[] input = "data".getBytes();       
    MessageDigest md = MessageDigest.getInstance("SHA-256");
    byte[] thedigest = md.digest("ENCRYPTION_KEY".getBytes("UTF-8"));
    SecretKeySpec skc = new SecretKeySpec(Arrays.copyOf(thedigest, 16), "AES");
    Cipher cipher = Cipher.getInstance("AES");
    cipher.init(Cipher.ENCRYPT_MODE, skc);
    
    byte[] cipherText = new byte[cipher.getOutputSize(input.length)];
    int ctLength = cipher.update(input, 0, input.length, cipherText, 0);
    ctLength += cipher.doFinal(cipherText, ctLength);
        
    String encryptData = Base64.getUrlEncoder().encodeToString(cipherText);
    // Base64.encodeToString(cipherText, Base64.DEFAULT);

    System.out.println("encryptString:.......... "+encryptData);

NODEJS:

var crypto = require('crypto');
let keyStr = "ENCRYPTION_KEY";
var text = 'infodba';
var hashPwd =crypto.createHash('sha256').update(keyStr,'utf8').digest();
var key=[] ;
for (i = 0; i < 16; i++) {
  key[i] = hashPwd[i];
}
var keyBuffer = new Buffer(hashPwd);
var decipherIV = crypto.createDecipheriv('aes-256-ecb', keyBuffer,'');
var cipherTxtBuffer = new Buffer('encryptedData','hex');
var output = decipherIV.update(cipherTxtBuffer);
output= output+decipherIV.final();
console.log("Dec Output "+output);

    

While running the node JS code getting error

internal/crypto/cipher.js:174 const ret = this[kHandle].final(); ^

Error: error:0606506D:digital envelope routines:EVP_DecryptFinal_ex:wrong final block length at Decipheriv.final (internal/crypto/cipher.js:174:29) at deciphers (D:\TestJS\index.js:32:30)

Topaco
  • 40,594
  • 4
  • 35
  • 62
Shik
  • 31
  • 1

1 Answers1

1

The Java code uses only the first 16 bytes of the SHA256 hash as key, so the hash has to be truncated accordingly:

var keyStr = 'ENCRYPTION_KEY';
var hashPwd = crypto.createHash('sha256').update(keyStr, 'utf8').digest();
var keyBuffer = Buffer.from(hashPwd.subarray(0, 16));

A 16 bytes key corresponds to AES-128, so 'aes-128-ecb' has to be used for decryption (and not 'aes-256-ecb'). Furthermore, the ciphertext is Base64url encoded, so it has to be Base64url decoded (and not hex decoded):

var encryptedData = 'dkcvstQcGMQUJ1EJbHs3eY6j_0DjWqYTDGmedmUZwWs=';
var decipherIV = crypto.createDecipheriv('aes-128-ecb', keyBuffer,'');
var output = Buffer.concat([decipherIV.update(encryptedData, 'base64url'), decipherIV.final()]);

output is a buffer and may have to be UTF-8 decoded:

console.log(output.toString('utf8')); // ยง3.1: The fee is $3,000.

With these changes, the NodeJS code decrypts the ciphertext created with the Java code.


Security:

  • The ECB mode is insecure and should not be used. Nowadays authenticated encryption is usually applied (such as GCM), or at least a mode with an IV (such as CBC).
  • Using a (fast) digest like SHA256 as key derivation is insecure. More secure is a dedicated key derivation function like Argon2 or PBKDF2.
  • In the Java code, the Cipher object is instantiated with the algorithm ("AES") alone. This is not robust because then the provider-dependent default values are applied for mode and padding. For SunJCE, these are ECB and PKCS#5 padding, to which the NodeJS code is tailored. For other providers this may differ, so that the NodeJS code would then no longer be compatible. Therefore, it should be fully specified, e.g. "AES/ECB/PKCS5Padding".
  • Similarly, when encoding the plaintext with getBytes(), the encoding should be specified, otherwise the platform-dependent default encoding is used.
Topaco
  • 40,594
  • 4
  • 35
  • 62