aes.js: шифрование и дешифрование различными ключами - почему это успешно сделано? - PullRequest
0 голосов
/ 09 апреля 2019

Почему я могу расшифровать текст, зашифрованный с помощью ключа , используя keyFake ?

Исходный код (используя bcrypt и aes-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 Ответ

1 голос
/ 09 апреля 2019

Здесь есть несколько проблем.

Во-первых: ДРУЗЬЯ НЕ ПОЗВОЛЯЮТ ДРУЗЬЯМ ЗАВЕРШИТЬ СВОЙ КРИПТО (по крайней мере, если вы хотите получить что-то безопасное). Вместо этого используйте высокоуровневые примитивы из некоторой стандартной библиотеки шифрования.

Тогда остальные вещи:

  • Основная проблема, которая позволяет вам расшифровывать вещи с неправильным ключом, состоит в том, что вы используете 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); или аналогичной в основной функции, делает дешифрование обратимым, , но код все равно не будет безопасным .

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