Pull to refresh

Comments 9

Вы про PKIJS в курсе? Или просто было интересно велосипед пописать?

Зачем тащить в проект лишнюю зависимость, если ты в состоянии написать нужную тебе реализацию?

440Кб unminified кода? И это при том, насколько Web Crypto даёт простое API для работы?

Либа вообще кажется весьма нишевой. Для простого encrypt/decrypt уж точно не подходит.
Да, библиотека только для тех, кто понимает, что такое PKI. Остальные пишут «tutorials» по технологии 7-ми летней давности.

скажите пожалуйста, что за магия в вашем коде? инструкция

key = await generateKey()

при каждом запуске генерирует новый ключ. Как сервер расшифровывает сообщения клиента, если ключ явно ему не передается?

И вообще, ключ шифрования AES - бинарная последовательность из 256 бит (согласно вашему коду - у вас там в параметрах стоит 256). Точное значение ключа нигде не фигурирует. как вообще работает ваше шифрование?

Пригодилось, спасибо.

Но нюансов много, конечно, о чём следовало бы написать.

Самый главный минус, который имхо следовало бы упомянуть, это то, что window.crypto.subtle недоступно при подключении через HTTP, оно обязательно требует установки сертификата и работы через HTTPS (что на мой взгляд какой-то дебилизм), а это не всегда нужно и удобно (для внутренних сервисов, например).

В итоге приходится использовать сторонние либы. Я решил через forge.

// ArrayBuffer -> Base64
const pack = buffer => window.btoa(String.fromCharCode.apply(null, new Uint8Array(buffer)));

// Base64 -> ArrayBuffer
const unpack = (base64) => {
  const binaryString = window.atob(base64);
  const bytes = new Uint8Array(binaryString.length);
  for (let i = 0; i < binaryString.length; i++) {
    bytes[i] = binaryString.charCodeAt(i);
  }
  return bytes;
};

const key = unpack('BASE64_KEY');

const decrypt = (ciphertext, key, iv) => {
  const decipher = forge.cipher.createDecipher('AES-CTR', forge.util.createBuffer(key));
  decipher.start({ iv: forge.util.createBuffer(iv) });
  decipher.update(forge.util.createBuffer(ciphertext));
  decipher.finish();
  // Получаем бинарные данные и конвертируем в UTF-8, иначе побъётся русский текст
  const bytes = decipher.output.getBytes();
  const decoder = new TextDecoder('utf-8');
  return decoder.decode(new Uint8Array(forge.util.binary.raw.decode(bytes)));
};

const getAndDecryptMsg = async () => {
  const output = document.querySelector('.output');

  const res = await fetch('/bidzaar/secure-api')
  const { data, v: vector } = await res.json()
  // Распаковка данных
  const msg = decrypt(unpack(data), key, unpack(vector));
  output.innerHTML = `<div>${objectToHTML(JSON.parse(msg))}</div>`
}

На стороне NodeJS, соответственно:

const pack = (buffer) => Buffer.from(buffer).toString('base64'); // Из буфера в base64
const unpack = (packed) => Buffer.from(packed, 'base64'); // Из base64 в буфер

const generateKey = () => crypto.randomBytes(16); // 128-битный ключ

// Функция шифрования
const encrypt = (data, key, iv) => {
  const cipher = crypto.createCipheriv('aes-128-ctr', key, iv);
  let encrypted = Buffer.concat([cipher.update(data), cipher.final()]);
  return encrypted; // Возвращает Buffer
};

const encryptAndSendMsg = async (msg) => {
  const iv = generateKey();
  const key = unpack('LnP02IO8s638ThgOQTvo6Q==');
  const encryptedData = await encrypt(Buffer.from(msg, 'utf8'), key, iv);
  return {
    data: pack(encryptedData), // Данные в base64
    v: pack(iv)
  };
}

Вы про этот проект https://github.com/digitalbazaar/forge?

Приятно видеть, что на свете еще остались люди, способные делать библиотеки со вменяемым интерфейсом. Спасибо за наводку, от души плюс куда следует!

так и не понял почему у webcryptoapi такой мудацкий интерфейс. Одни функции возвращают промисы, другие - сразу дают результат. Тот же getRandomValues спроектирован в синхронном стиле, а вот хеш считается asynv'ом. Хотя по логике должно быть наоборот (случайные числа поставляются аппаратурой и здесь могут быть недетерминированные задержки, в отличие от простой как репа функции вычисления хеша). А эти все пляски с бубном вокруг шифрования. Вы же привели простой и лаконичный код.

Благодаря разработчикам интерфейсов webcryptoapi я неожиданно узнал, что, оказывается при создании ключа для симметричного шифра AES нужно указывать опции ['encrypt', 'decrypt'], а то работать не будет. Странно, везде до этого писали что один и тот же ключ годится как для шифрования так и для расшифровки. И вообще ключ - это какой то сверхсекретный магический объект, который должен быть обязательно сформирован специальными функциями generateKey или importKey. О как! Я-то наивный думал, что подойдет любая числовая 128-ми, 192- или 256-битная последовательность. Век живи - век учись!

Так что когда увидел ваш образец, воскликнул: "А что так можно было?" Спасибо.

Да, про неё

У них свои какие-то там проблемы с документацией, нормальные примеры пришлось очень долго искать чтоб понять как с ней работать, но в итоге оно реально удобней чем webcryptoapi, мне оно тоже не нравится совершенно...

Sign up to leave a comment.

Articles