问题
Using the following node js:
var crypto = require('crypto');
var encrypt = function (input, password, callback) {
var m = crypto.createHash('md5');
m.update(password);
var key = m.digest('hex');
m = crypto.createHash('md5');
m.update(password + key);
var iv = m.digest('hex');
console.log(iv);
var data = new Buffer(input, 'utf8').toString('binary');
var cipher = crypto.createCipheriv('aes-256-cbc', key, iv.slice(0,16));
var encrypted = cipher.update(data, 'binary') + cipher.final('binary');
var encoded = new Buffer(encrypted, 'binary').toString('base64');
callback(encoded);
};
var decrypt = function (input, password, callback) {
// Convert urlsafe base64 to normal base64
input = input.replace(/\-/g, '+').replace(/_/g, '/');
// Convert from base64 to binary string
var edata = new Buffer(input, 'base64').toString('binary');
// Create key from password
var m = crypto.createHash('md5');
m.update(password);
var key = m.digest('hex');
// Create iv from password and key
m = crypto.createHash('md5');
m.update(password + key);
var iv = m.digest('hex');
// Decipher encrypted data
var decipher = crypto.createDecipheriv('aes-256-cbc', key, iv.slice(0,16));
var decrypted = decipher.update(edata, 'binary') + decipher.final('binary');
var plaintext = new Buffer(decrypted, 'binary').toString('utf8');
callback(plaintext);
};
To execute I ran this:
encrypt("uWeShxRrCKyK4pcs", "secret", function (encoded) {
console.log(encoded);
decrypt(encoded, "secret", function (output) {
console.log(output);
});
});
Encrypting seems to work fine, but when I try to decrypt, I receive the following error:
Error: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt at Error (native) at Decipheriv.Cipher.final (crypto.js:202:26)
I am pretty new to cryptography, so don't really know why I am receiving this error. I just need to get it fixed for now.
回答1:
You mixed up two different encodings. See
cipher.update(data[, input_encoding][, output_encoding])
and
cipher.final([output_encoding])
and now look at
var encrypted = cipher.update(data, 'binary') + cipher.final('binary');
but it should be
var encrypted = cipher.update(data, 'binary', 'binary') + cipher.final('binary');
The issue is that cipher.update(data, 'binary')
outputs a buffer which automatically stringifies to a Hex-encoded string instead of a "binary"-string.
Anyway, there is so much wrong with this code that you should start over and simply use an existing library that is highly opinionated.
You must have a random IV which is prepended to the ciphertext in order to reach semantic security.
A password has low entropy and cannot be used as a key. A single MD5 invocation doesn't change that fact. Key derivation from a password is supposed to be slow, so use a known scheme such as PBKDF2, bcrypt, scrypt or Argon2 (increasing security) with a high iteration count/cost factor. Don't forget the salt.
Authenticate your ciphertext with a message authentication code such as HMAC-SHA256 in an encrypt-then-MAC scheme. Otherwise, an attacker may manipulate ciphertexts and you won't even be able to detect changes. First step to losing data with a padding oracle attack.
回答2:
I found out the reason for this, is using different key's or iv's for encryption & decryption. We must use the same key & iv used for encryption, for decrypting the content. An only workaround is to save the iv & key in an array used during encrypting the data or concatenating iv & key with the encrypted data with the help of separators
Example One:
function encrypt(text) {
let cipher = crypto.createCipheriv('aes-256-cbc', Buffer.from(key), iv);
let encrypted = cipher.update(text);
encrypted = Buffer.concat([encrypted, cipher.final()]);
return encrypted.toString('hex') + ':' + iv.toString('hex') + '=' +
key.toString('hex');
//returns encryptedData:iv=key
}
function decrypt(text) {
let iv = Buffer.from((text.split(':')[1]).split('=')[0], 'hex')//will return iv;
let enKey = Buffer.from(text.split('=')[1], 'hex')//will return key;
let encryptedText = Buffer.from(text.split(':')[0], 'hex');//returns encrypted Data
let decipher = crypto.createDecipheriv('aes-256-cbc', Buffer.from(enKey), iv);
let decrypted = decipher.update(encryptedText);
decrypted = Buffer.concat([decrypted, decipher.final()]);
return decrypted.toString();
//returns decryptedData
}
Example Two:
function encrypt(text) {
let cipher = crypto.createCipheriv('aes-256-cbc', Buffer.from(key), iv);
let encrypted = cipher.update(text);
encrypted = Buffer.concat([encrypted, cipher.final()]);
return {
"encryptedData": encrypted.toString('hex'),
"iv" : iv.toString('hex'),
"key" : key.toString('hex');
//returns an Array of key, iv & encryptedData
}
}
function decrypt(text) {
let iv = Buffer.from((text.iv, 'hex')//will return iv;
let enKey = Buffer.from(text.key, 'hex')//will return key;
let encryptedText = Buffer.from(text.encryptedData, 'hex');//returns encrypted Data
let decipher = crypto.createDecipheriv('aes-256-cbc', Buffer.from(enKey), iv);
let decrypted = decipher.update(encryptedText);
decrypted = Buffer.concat([decrypted, decipher.final()]);
return decrypted.toString();
//returns decryptedData
}
来源:https://stackoverflow.com/questions/37997354/evp-decryptfinal-exbad-decrypt-when-using-node-js