-
Notifications
You must be signed in to change notification settings - Fork 2.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Equivalent node:crypto commands for CryptoJS (to migrate after deprecation) #468
Comments
I found this example: 'use strict';
const assert = require('node:assert');
const crypto = require('node:crypto');
const CryptoJS = require('crypto-js');
const secret = 'secret';
const plainText = 'message';
class CryptoHelper {
decryptAES(encryptedText, secret) {
// From https://gist.github.com/schakko/2628689?permalink_comment_id=3321113#gistcomment-3321113
// From https://gist.github.com/chengen/450129cb95c7159cb05001cc6bdbf6a1
const cypher = Buffer.from(encryptedText, 'base64');
const salt = cypher.slice(8, 16);
const password = Buffer.concat([Buffer.from(secret, 'binary'), salt]);
const md5Hashes = [];
let digest = password;
for (let i = 0; i < 3; i++) {
md5Hashes[i] = crypto.createHash('md5').update(digest).digest();
digest = Buffer.concat([md5Hashes[i], password]);
}
const key = Buffer.concat([md5Hashes[0], md5Hashes[1]]);
const iv = md5Hashes[2];
const contents = cypher.slice(16);
const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
return decipher.update(contents) + decipher.final();
}
}
const cryptoHelper = new CryptoHelper;
const encrypted = CryptoJS.AES.encrypt(plainText, secret, { mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 });
const encryptedText = encrypted.toString();
console.log('encrypted', encryptedText);
const decryptedText = cryptoHelper.decryptAES(encryptedText, secret);
console.log('decrypted', decryptedText);
assert.equal(plainText, decryptedText); |
@a-tortevois Thank you for the example! I'm going to test it later this afternoon, but from a quick glance I'm a little worried about the padding: I'm wondering if that difference will make my encrypted strings non-decryptable by |
It work fine with your string, secret : console.log('decrypted:', cryptoHelper.decryptAES('U2FsdGVkX18ZLpNMrgcEPbbEfE2c6h3E9kc0GRLE4pU=', 'V7gRKWw4uz6QVH7cGHqcUEPHpr8CfqD4LTckiTpmdeeDzS423Zc7zaBngvpwBv6Y'));
|
This worked perfectly, thank you so much @a-tortevois!! |
So CryptoJS just dropped a bomb: everything they do by default is not as strong as it could be. Oh and by the way, the entire library is now deprecated. :( GHSA-xwcq-pm8m-c4vf Unfortunately we can't just upgrade to the latest release 4.2.0 because the hashing algorithm has changed, and a user would no longer be able to login: the default hash generated by CryptoJS 4.2.0 won't match the hash generated by CryptoJS 4.1.0. Note that this is only an issue if someone got the contents of your database and wanted to figure out user passwords (but it still cost [$45,000 per password](https://eprint.iacr.org/2020/014.pdf) apparently?). In the wake of this CVE we're going to convert dbAuth to use the built-in `node:crypto` library instead, with more sensible default configuration. There are two areas where we use the crypto libs: 1. Hashing the user's password to store in the DB and compare on login 2. Encrypting/decrypting the session data in a cookie We're going to do this in a non-breaking way by supporting *both* the original CryptoJS-derived values, and the new `node:crypto` ones. The alternative would be to require every user to change their password, which seems like a non-starter. 1. On signup, store the hashedPassword using the new `node:crypto` algorithm 2. On login, compare the user's hashedPassword using the `node:crypto` algorithm: * If a match is found, user is logged in * If a match fails, fall back to the original CryptoJS algorithm (but using the `node:crypto` implementation) * If a match is found, update the `hashedPassword` in the database to the new algorithm, user is logged in * If a match is still not found, the user entered the wrong password. Likewise for cookies and login: 1. When encrypting the user's session, always use the new `node:crypto` algorithm 2. When decrypting the user's session, first try with `node:crypto` * If decrypting works, user is logged in * If decrypting fails, try the older CryptoJS algorithm ([I haven't figured how](brix/crypto-js#468) to use `node:crypto` to decrypt something that was encrypted with CryptoJS yet, so we'll need to keep the dependency on CryptoJS around for now) * If decrypting works, re-encrypt the cookie using the new `node:crypto` algorithm, user is logged in * If decrypting still fails, the session is invalid (someone tampered with the cookie) so log them out ## Notifying Users We could announce in the Release Notes that if a platform wants the absolute safest route, they should change their `SESSION_SECRET` *and* have users change their password if, for example, they suspect that their database may have been compromised before our release. The next most secure thing would be to just change `SESSION_SECRET` which would log everyone out, and on next login their password will get re-hashed with the new algorithm. But the default for most people will probably be to just go about business as usual, and as time goes by more and more users' passwords will be re-hashed on login. Related to #9337 #9338 #9339 #9340 --------- Co-authored-by: Dominic Saadi <[email protected]>
So CryptoJS just dropped a bomb: everything they do by default is not as strong as it could be. Oh and by the way, the entire library is now deprecated. :( GHSA-xwcq-pm8m-c4vf Unfortunately we can't just upgrade to the latest release 4.2.0 because the hashing algorithm has changed, and a user would no longer be able to login: the default hash generated by CryptoJS 4.2.0 won't match the hash generated by CryptoJS 4.1.0. Note that this is only an issue if someone got the contents of your database and wanted to figure out user passwords (but it still cost [$45,000 per password](https://eprint.iacr.org/2020/014.pdf) apparently?). In the wake of this CVE we're going to convert dbAuth to use the built-in `node:crypto` library instead, with more sensible default configuration. There are two areas where we use the crypto libs: 1. Hashing the user's password to store in the DB and compare on login 2. Encrypting/decrypting the session data in a cookie We're going to do this in a non-breaking way by supporting *both* the original CryptoJS-derived values, and the new `node:crypto` ones. The alternative would be to require every user to change their password, which seems like a non-starter. 1. On signup, store the hashedPassword using the new `node:crypto` algorithm 2. On login, compare the user's hashedPassword using the `node:crypto` algorithm: * If a match is found, user is logged in * If a match fails, fall back to the original CryptoJS algorithm (but using the `node:crypto` implementation) * If a match is found, update the `hashedPassword` in the database to the new algorithm, user is logged in * If a match is still not found, the user entered the wrong password. Likewise for cookies and login: 1. When encrypting the user's session, always use the new `node:crypto` algorithm 2. When decrypting the user's session, first try with `node:crypto` * If decrypting works, user is logged in * If decrypting fails, try the older CryptoJS algorithm ([I haven't figured how](brix/crypto-js#468) to use `node:crypto` to decrypt something that was encrypted with CryptoJS yet, so we'll need to keep the dependency on CryptoJS around for now) * If decrypting works, re-encrypt the cookie using the new `node:crypto` algorithm, user is logged in * If decrypting still fails, the session is invalid (someone tampered with the cookie) so log them out We could announce in the Release Notes that if a platform wants the absolute safest route, they should change their `SESSION_SECRET` *and* have users change their password if, for example, they suspect that their database may have been compromised before our release. The next most secure thing would be to just change `SESSION_SECRET` which would log everyone out, and on next login their password will get re-hashed with the new algorithm. But the default for most people will probably be to just go about business as usual, and as time goes by more and more users' passwords will be re-hashed on login. Related to #9337 #9338 #9339 #9340 --------- Co-authored-by: Dominic Saadi <[email protected]>
So CryptoJS just dropped a bomb: everything they do by default is not as strong as it could be. Oh and by the way, the entire library is now deprecated. :( GHSA-xwcq-pm8m-c4vf Unfortunately we can't just upgrade to the latest release 4.2.0 because the hashing algorithm has changed, and a user would no longer be able to login: the default hash generated by CryptoJS 4.2.0 won't match the hash generated by CryptoJS 4.1.0. Note that this is only an issue if someone got the contents of your database and wanted to figure out user passwords (but it still cost [$45,000 per password](https://eprint.iacr.org/2020/014.pdf) apparently?). In the wake of this CVE we're going to convert dbAuth to use the built-in `node:crypto` library instead, with more sensible default configuration. There are two areas where we use the crypto libs: 1. Hashing the user's password to store in the DB and compare on login 2. Encrypting/decrypting the session data in a cookie We're going to do this in a non-breaking way by supporting *both* the original CryptoJS-derived values, and the new `node:crypto` ones. The alternative would be to require every user to change their password, which seems like a non-starter. 1. On signup, store the hashedPassword using the new `node:crypto` algorithm 2. On login, compare the user's hashedPassword using the `node:crypto` algorithm: * If a match is found, user is logged in * If a match fails, fall back to the original CryptoJS algorithm (but using the `node:crypto` implementation) * If a match is found, update the `hashedPassword` in the database to the new algorithm, user is logged in * If a match is still not found, the user entered the wrong password. Likewise for cookies and login: 1. When encrypting the user's session, always use the new `node:crypto` algorithm 2. When decrypting the user's session, first try with `node:crypto` * If decrypting works, user is logged in * If decrypting fails, try the older CryptoJS algorithm ([I haven't figured how](brix/crypto-js#468) to use `node:crypto` to decrypt something that was encrypted with CryptoJS yet, so we'll need to keep the dependency on CryptoJS around for now) * If decrypting works, re-encrypt the cookie using the new `node:crypto` algorithm, user is logged in * If decrypting still fails, the session is invalid (someone tampered with the cookie) so log them out We could announce in the Release Notes that if a platform wants the absolute safest route, they should change their `SESSION_SECRET` *and* have users change their password if, for example, they suspect that their database may have been compromised before our release. The next most secure thing would be to just change `SESSION_SECRET` which would log everyone out, and on next login their password will get re-hashed with the new algorithm. But the default for most people will probably be to just go about business as usual, and as time goes by more and more users' passwords will be re-hashed on login. Related to #9337 #9338 #9339 #9340 --------- Co-authored-by: Dominic Saadi <[email protected]>
To add to this we are also using CryptoJs and we would like to replace existing cryptoJs implementation with web crypto APIs. For text and objects, it is working fine but with files, we are first converting into wordArray using
what is something equivalent we can use to achieve the same result so that hash generated by web crypto API is same |
Hi there, |
what about encryption?.......... chatgpt can't do it....... |
Of course, encryption is simply the reverse operation encryptAES(plainText, secret) {
const salt = crypto.randomBytes(8);
const password = Buffer.concat([Buffer.from(secret, 'binary'), salt]);
const hash = [];
let digest = password;
for (let i = 0; i < 3; i++) {
hash[i] = crypto.createHash('md5').update(digest).digest();
digest = Buffer.concat([hash[i], password]);
}
const keyDerivation = Buffer.concat(hash);
const key = keyDerivation.subarray(0, 32);
const iv = keyDerivation.subarray(32);
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
return Buffer.concat([
Buffer.from('Salted__', 'utf8'),
salt,
cipher.update(plainText),
cipher.final()
]).toString('base64');
} |
@a-tortevois Thanks... but it doesn't work.. I guess my cryptojs ecnryption is different or sth.... function checkAndConvertToHex(str) {
if (/^\d+$/.test(str)) {
// eslint-disable-next-line no-undef
let hex = BigInt(str).toString(16).padStart(64, '0');
return '0x' + hex;
}
else {
return str;
}
}
function is_odd(num) {
return num % 2 !== 0;
}
export const encryptPrivateKey2 = (privateKey: string, password: string) => {
const padded = checkAndConvertToHex(privateKey);
let privateKeyBytes;
if (is_odd(padded.length)) {
privateKeyBytes = CryptoJS.enc.Utf8.parse(padded);
} else {
privateKeyBytes = CryptoJS.enc.Hex.parse(padded);
}
const key = CryptoJS.PBKDF2(password, CryptoJS.SHA256(password), { keySize: 256 / 32 });
const ciphertext = CryptoJS.AES.encrypt(privateKeyBytes, key, { mode: CryptoJS.mode.ECB });
return (is_odd(padded.length) ? 'UTF8' : '') + ciphertext.toString();
}; |
Be careful, you use CryptoJS.mode.ECB, mine is with aes-256-cbc algorithm |
could you say what's more secured? if it's even correct to compare them? |
@a-tortevois Thank you! both encrypt and decrypt work fine as expected 🚀 |
Hi folks! I have decided to create this NPM module with a similar API: https://github.com/RaisinTen/aes-crypto-js to make it easier to migrate away from this deprecated library. Thanks to the conversation above, the library uses Node.js' builtin crypto module internally and is far easier to maintain. Feel free to give it a try, post issues and submit PRs, thanks! |
We are using CryptoJS in our code but now that 4.2.0 comes with a big deprecation notice we plan on moving off. We'd like to eliminate the CryptoJS lib completely, and just use
node:crypto
going forward, but I can't figure out the proper incantation ofnode:crypto
commands to decrypt something that was encrypted with CryptoJS:We'd like to be able to decrypt using
node:crypto
and then re-encrypt using a more secure version, with aniv
and all that good stuff. I've tried several different iterations of this code, with 6 different algorithms, but none of them are able to decrypt:The
node:crypto
docs state:Which sounds similar to what CryptoJS is doing, but maybe not similar enough. :(
I've tried truncating the
secret
to only 32 characters (tried both first 32 and last 32) but it didn't help.Any ideas of what else I could try? For testing, here's the
encText
andsecret
that should decrypt toHello, world
:The text was updated successfully, but these errors were encountered: