aes.js:使用不同的密钥进行加密和解密 - 为什么会成功?

aes.js: encryption and decryption with the different keys - why it's done successfully?

为什么我可以使用 keyFake 解密用 key 加密的文本?

源代码(使用bcryptaes-js):

const bcrypt = require('bcrypt');
const aesjs = require('aes-js');

(async () => {
  let myPlaintextPassword = "pass";
  let myPlaintextPasswordFake = "sdfs6654df";
  let saltRounds = 10;
  let hash = await bcrypt.hash(myPlaintextPassword, saltRounds);
  let key = Buffer.from({ arrayBuffer: hash, length: 32 });

  let hashFake = await bcrypt.hash(myPlaintextPasswordFake, saltRounds);
  let keyFake = Buffer.from({ arrayBuffer: hashFake, length: 32 });

  // Convert text to bytes
  var text = "ЧЕРТ ВОЗЬМИ, КАК ЖЕ ЭТО СЕКРЕТНО!";
  console.log(text);
  var textBytes = aesjs.utils.utf8.toBytes(text);

  // The counter is optional, and if omitted will begin at 1
  var aesCtr = new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(5));
  var encryptedBytes = aesCtr.encrypt(textBytes);

  // To print or store the binary data, you may convert it to hex
  var encryptedHex = aesjs.utils.hex.fromBytes(encryptedBytes);
  console.log("encrypted " + encryptedHex);

  // "a338eda3874ed884b6199150d36f49988c90f5c47fe7792b0cf8c7f77eeffd87
  //  ea145b73e82aefcf2076f881c88879e4e25b1d7b24ba2788"

  // When ready to decrypt the hex string, convert it back to bytes
  var encryptedBytes = aesjs.utils.hex.toBytes(encryptedHex);

  // The counter mode of operation maintains internal state, so to
  // decrypt a new instance must be instantiated.
  var aesCtr = new aesjs.ModeOfOperation.ctr(keyFake, new aesjs.Counter(5));
  var decryptedBytes = aesCtr.decrypt(encryptedBytes);

  // Convert our bytes back into text
  var decryptedText = aesjs.utils.utf8.fromBytes(decryptedBytes);
  console.log("decrypted " + decryptedText);
})();

结果如下:

1

谁能解释为什么代码会这样?我用别的key不是应该看到废话吗?

这里有几个问题。

首先:朋友们不要让朋友们滚动他们自己的加密货币(至少如果你想得到安全的东西的话不要)。请改用一些现成的加密库中的高级原语。

然后,其他的东西:

  • 导致您使用错误密钥解密的根本问题是您使用 Buffer.from() 错误,最终得到密钥 <Buffer 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00>。将该调用更改为 Buffer.from(hash).slice(0, 32) 可为您提供从 bcrypt.hash() 结果派生的关键类型,但是:
  • 您使用 bcrypt.hash() 的方式,它永远不会 returns 相同的字符串用于相同的输入字符串,这使得即使您知道正确的密码也无法解密数据。此外,函数 returns 是一个类似于 b$z3X6QVxZtl4JmrkH2u7rV.bVq0vFUY9XSrTKVnoyZ7s8X4cybmox6 的字符串,就目前情况而言(即使 Buffer.from() 的用法正确),您最终只会使用前 32 个字符,这可能不是你想要的。
  • 您的加密方案缺少身份验证;您无法知道您是否已正确解密字符串。例如,通过固定的 Buffer.from() 调用,我得到(例如 – 这总是随机的 –) decrypted 嶥,벗Jꢣ틣FMnZhH줰]}H㥋z⮕gL⎕ 作为输出,并且在不知道原始明文的情况下,我不知道它是否是正确的解密结果。

这是我使用的重构代码。

saltRounds 更改为使用 const salt = await bcrypt.genSalt(10); 派生的盐字符串或在主函数中的类似内容使解密可逆,但代码仍然不安全.

"use strict";
const bcrypt = require("bcrypt");
const aesjs = require("aes-js");

async function deriveKey(password, saltRounds) {
  const hash = await bcrypt.hash(password, saltRounds);
  console.log("Hash:", hash);
  return Buffer.from(hash).slice(0, 32);
}

async function getEncryptionObject(password, saltRounds, counter) {
  const key = await deriveKey(password, saltRounds);
  console.log("Key:", key);
  return new aesjs.ModeOfOperation.ctr(key, new aesjs.Counter(counter));
}

async function encrypt(text, password, saltRounds, counter = 5) {
  const aesCtr = await getEncryptionObject(password, saltRounds, counter);
  const textBytes = aesjs.utils.utf8.toBytes(text);
  const encryptedBytes = aesCtr.encrypt(textBytes);
  return aesjs.utils.hex.fromBytes(encryptedBytes);
}

async function decrypt(encryptedHex, password, saltRounds, counter = 5) {
  const aesCtr = await getEncryptionObject(password, saltRounds, counter);
  const encryptedBytes = aesjs.utils.hex.toBytes(encryptedHex);
  const decryptedBytes = aesCtr.decrypt(encryptedBytes);
  return aesjs.utils.utf8.fromBytes(decryptedBytes);
}

(async () => {
  const encryptionPassword = "pass";
  const decryptionPassword = "sdfs6654df";
  const saltRounds = 10;
  const text = "ЧЕРТ ВОЗЬМИ, КАК ЖЕ ЭТО СЕКРЕТНО!";
  console.log("original: " + text);
  const encryptedHex = await encrypt(text, encryptionPassword, saltRounds, 5);
  console.log("encrypted " + encryptedHex);
  const decryptedText = await decrypt(
    encryptedHex,
    decryptionPassword,
    saltRounds,
    5,
  );
  console.log("decrypted " + decryptedText);
})();