1

I was developing server with Node.js and client with Ionic framework

I made API for login request from client

when client request login, sends encrypted id and password string

and server decrypt received id and password string and check validation

I used crypto-js(https://code.google.com/archive/p/crypto-js/) library for client encryption

client encryption code below

var secret = 'abcdefghijklmnopqrstuvwxyz123456';
var id = "someId";
var encrypted = CryptoJS.AES.encrypt(id, password);
console.log(encrypted.toString());  // U2FsdGVkX19EfjjBwydSZL509wKl5TEX+4f3vakEejU=

For server-side decryption I used node built-in crypto module

const crypto = require('crypto');
var method = 'aes256';
var secret = 'abcdefghijklmnopqrstuvwxyz123456';
var id = "U2FsdGVkX19EfjjBwydSZL509wKl5TEX+4f3vakEejU=" // suppose we received with no loss
var decipher = crypto.createDecipher(method, secret);
decipher.update(id,'base64','utf8');
var deciphered = decipher.final('utf8');
console.log(deciphered);

server-side decrypt code crash with error message below

crypto.js:153
  var ret = this._handle.final();
                         ^

Error: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt
    at Error (native)
    at Decipher.Cipher.final (crypto.js:153:26)
    at Object.<anonymous> (...\routes\index.js:33:27)
    at Module._compile (module.js:409:26)
    at Object.Module._extensions..js (module.js:416:10)
    at Module.load (module.js:343:32)
    at Function.Module._load (module.js:300:12)
    at Module.require (module.js:353:17)
    at require (internal/module.js:12:17)
    at Object.<anonymous> (...\app.js:18:14)
    at Module._compile (module.js:409:26)
    at Object.Module._extensions..js (module.js:416:10)
    at Module.load (module.js:343:32)
    at Function.Module._load (module.js:300:12)
    at Module.require (module.js:353:17)
    at require (internal/module.js:12:17)

As error message was 'bad decrypt' so I tried to encrypt same text with each library

[crypto-js]

var secret = 'abcdefghijklmnopqrstuvwxyz123456';
var id = "someId";
var encrypted = CryptoJS.AES.encrypt(id, password);
console.log(encrypted.toString());  // U2FsdGVkX19EfjjBwydSZL509wKl5TEX+4f3vakEejU=

[crypto module]

const crypto = require('crypto');
var method = 'aes256';
var secret = 'abcdefghijklmnopqrstuvwxyz123456';
var id = "someId" 
var cipher= crypto.createCipher(method, secret);
cipher.update(id,'base64','utf8');
var ciphered = decipher.final('utf8');
console.log(ciphered.toString()); // WAsd61C2bfG7UbO5STo13A==

I found out result of library is different

plain text : 'someId'
crpyto-js  : 'U2FsdGVkX19EfjjBwydSZL509wKl5TEX+4f3vakEejU='
crpyto module : 'WAsd61C2bfG7UbO5STo13A=='

I tried to understand the source code of each library

but it was too complicate so, I couldn't understand

I want to know how each library's encrytion works and what cause the different result

  • These two answers of mine handle both directions: [CryptoJS to node.js](http://stackoverflow.com/a/28361216/1816580) and [node.js to CryptoJS](http://stackoverflow.com/a/33942660/1816580) – Artjom B. Aug 11 '16 at 18:37
  • you're not using a valid AES key, so both systems attempt to make it the right length, apparently with different strategies. – dandavis Aug 11 '16 at 21:09
  • @dandavis That's exactly the difference. Both aren't using any key, but they assume that the passed "key" is a password and then derive the actual key from the password. The only difference is that CryptoJS uses a random salt and node.js doesn't. That's what my answer behind my first link is saying. Sadly, I cannot propose a duplicate. – Artjom B. Aug 11 '16 at 21:26

2 Answers2

0

You are using two different systems which may have different defaults. Do not rely on defaults, but explicitly specify everything to be the same on both sides. Crypto is designed to fail if even the smallest thing does not match. You will need to specify the character to byte mapping used, the crypto mode, the IV (if needed), the key and the padding method.

Your different outputs are different lengths, so initially I suspect that padding is the first thing to look at. Set both sides to PKCS#7 padding and see if that helps.

For further diagnosis, check that the key and IV are byte-for-byte the same on each side.

rossum
  • 15,344
  • 1
  • 24
  • 38
0

All parameters must be specified and correct. Do not rely on defaults, they are implementation dependent and will very between implementations.

In general you should use:

  1. CBC mode with a random iv, on encryption create a random iv, prepend it to the encrypted data for use on decryption. For AES the iv should be 16-bytes, the block size. Do not use ECB mode, it is insecure, see ECB mode, scroll down to the Penguin.

  2. PKCS#7 padding (sometimes called PKCS#5). This is needed because AES is a block cipher and input and output must be an exact multiple of the block size. This padding will be automatically added during encryption and removed during decryption.

  3. Ensure the iv and key are exactly the correct size.

  4. For debugging display all inputs and outputs in hexadecimal. It can be helpful during debugging to dump all inputs and outputs just prior to and after encryption and decryption. Hexadecimal allows one to see each byte, Base64 combines 3 bytes into 4 bytes and makes it harder to understand.

When all this is exactly the same the outputs will also be exactly the same.

zaph
  • 111,848
  • 21
  • 189
  • 228