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

时间:2019-04-09 11:44:16

标签: node.js aes

为什么我可以使用 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);
})();

这是结果:

screenshot 1

谁能解释为什么代码如此行为?如果我使用另一个密钥,我应该看不废话吗?

1 个答案:

答案 0 :(得分:1)

这里有几个问题。

首先:朋友不要让朋友滚动自己的密码(至少如果您想要获得安全的东西,至少不要这样)。改用一些现成的加密库中的高级基元。

然后,其他内容:

  • 让您使用错误的密钥解密内容的根本原因是,您使用的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()的方式,它永远不会为相同的输入字符串返回相同的字符串,即使知道正确的密码短语也无法解密数据。另外,该函数返回一个类似$2b$10$z3X6QVxZtl4JmrkH2u7rV.bVq0vFUY9XSrTKVnoyZ7s8X4cybmox6的字符串,并且按目前情况(即使正确使用了Buffer.from()),您最终只能使用前32个字符,这可能不是你想要的。
  • 您的加密方案缺乏身份验证;您不知道您是否正确解密了字符串。例如,使用固定的Buffer.from()调用,我得到(例如–始终是随机的–)decrypted 嶥,벗Jꢣ틣FMnZhH줰]}H㥋z⮕gL⎕作为输出,并且在不知道原始明文的情况下,我不知道它是否是正确的解密结果。

这是我使用的重构代码。

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

"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);
})();