I am encrypting a string using Advanced Encryption Standard (AES) in the browser with 'cypto-js' and need to decrypt it on the server with Node 'crypto'.
I can encrypt / decrypt just fine with 'crypto-js' alone, however when I try to decrypt with 'crypto' (Node) using 'crypto.createDecipher' I get error messages to the effect of 'bad decrypt' or 'wrong block size' depending on what I try.
ex: using just 'crypto-js' - works fine
crypto-js
const cypherParams = CryptoJS.AES.encrypt('my message', 'passphrase')
const decrypted = CryptoJS.AES.decrypt(cypherParams, 'passphrase')
console.log(decrypted.toString(CryptoJS.enc.Utf8)) // 'my message' - works!
ex: encode with 'crypto-js' decode with 'crypto' - results in error
[client]
const cypherParams = CryptoJS.AES.encrypt('my message', 'passphrase')
[server]
const decipher = crypto.createDecipher('aes-256-cbc', 'passphrase');
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
// results in 'bad decrypt' or 'block size' error in console
console.log(decrypted); // this never executes
I have tried:
- Changing the encryption algorithm in decrypt to 192 or other (but 'crypto-js' docs say default is '256' is passphrase is used
- base64 encoding on the client. tried hex encoding as well
ex:
const cypherParams = CryptoJS.AES.encrypt('my message', 'passphrase')
const base64Encoded = cipherParams.toString(CryptoJS.enc.Base64)
and
const cypherParams = CryptoJS.AES.encrypt('my message', 'passphrase')
const cypherParams.ciphertext = cipherParams.toString(CryptoJS.enc.Base64)
- I am using 'crypto.createDecipher' rather than 'crypto.createDecipheriv' because I am stuck using Node v8.12.0 on this project
I think that's it...I appreciate any help or tips!
If the key is passed as a string,
CryptoJS.AES.encrypt()
uses the OpenSSL key derivation function (KDF)EVP_BytesToKey()
to derive a 32 bytes key (and a 16 bytes IV), i.e. indeed AES-256 is applied for encryption (here and here). During this process a random 8 bytes salt is generated, which ensures that each time a different key/IV pair results.The NodeJS method
crypto.createCipher()
uses the same KDF, but does not apply a salt, so that always the same key/IV pair is generated. Thereforecrypto.createDecipher()
does not take a salt into account either.Altogether, this means that the key pair generated when encrypting with
CryptoJS.AES.encrypt()
is different from the key pair generated when decrypting withcrypto.createDecipher()
and the decryption fails.As far as I know both methods do not offer the possibility to control whether a salt is used or not, so that the incompatibility cannot be eliminated.
One solution would therefore be to omit the built-in KDF (which is considered weak anyway, which in turn is why
crypto.createCipher()
/crypto.createDecipher()
are deprecated) and use a reliable KDF instead, e.g. PBKDF2 and work with the key/IV pair derived from it.On the CryptoJS side you have to pass key and IV as
WordArray
, on the NodeJS side you have to usecreate.createDecipheriv()
.The connection between encryption and decryption is the salt to be generated randomly during the key derivation. The salt is not secret, is usually concatenated with the ciphertext and passed to the recipient in this way.
You mention that the version you are using is Node v8.12.0 and therefore you cannot apply
crypto.createDecipheriv()
. Butcrypto.createDecipheriv()
is available since v0.1.94, so it should be available in your environment.Sample implementation for encryption on the client side (CryptoJS):
Sample implementation for decryption on the server side (NodeJS):