nodejs crypto Существует ли какой-либо зашифрованный и дешифрованный текст, должен каждый раз давать другое шифрование - PullRequest
0 голосов
/ 12 мая 2018

Я знаю AES 256 CBC с буфером, чтобы дать другое шифрование, но его длина 66 .вот мой код

  const crypto = require('crypto');
  const ENCRYPTION_KEY = 'Must256bytes(32characters)secret';
  const IV_LENGTH = 16;
  function encrypt(text) {
    let iv = crypto.randomBytes(IV_LENGTH);
    let cipher = crypto.createCipheriv('aes-256-cbc', new Buffer(ENCRYPTION_KEY), iv);
    let encrypted = cipher.update(text.toString());
    encrypted = Buffer.concat([encrypted, cipher.final()]);
    return iv.toString('hex') + 'XX' + encrypted.toString('hex');
  }
  function decrypt(text) {
    let textParts = text.split('XX');
    let iv = new Buffer(textParts.shift(), 'hex');
    let encryptedText = new Buffer(textParts.join('XX'), 'hex');
    let decipher = crypto.createDecipheriv('aes-256-cbc', new Buffer(ENCRYPTION_KEY), iv);
    let decrypted = decipher.update(encryptedText);
    try{
      decrypted = Buffer.concat([decrypted, decipher.final()]);
      return decrypted.toString();
    }catch(Err){
      return 'NULL';
    }
  }

Проблема в том, что длина данных шифрования составляет 66 даже для текста 1
Так есть ли какой-либо метод шифрования и дешифрования должен давать разные данные шифрования каждый разс менее чем 10 символов для текста равен 1 (согласно моему примеру)

Спасибо

Ответы [ 3 ]

0 голосов
/ 12 мая 2018

Да.Вы можете получить зашифрованный текст из 5 байтов (или 10 шестнадцатеричных символов) или менее.Но есть уловки для таких коротких шифротекстов.

В основном есть два пути.Начну с более простого.

Режим счетчика (CTR)

Вы можете использовать режим CTR (счетчик) с одноразовым номером.Шифрование X байтов в режиме CTR дает в результате ровно X байтов.Режим CTR не требует заполнения открытого текста в N раз больше размера блока.

Этот одноразовый номер (номер используется один раз) должен быть уникальным числом, или ваш открытый текств прямой опасности быть подверженным .Вы не можете просто положиться на случайное число;4-байтовое случайное число имеет высокую вероятность повторения из-за границы дня рождения .

Так что вам нужно либо хранить одноразовый номер отдельно, либо включать 4-байтовый одноразовый номер в ваш зашифрованный текст.Однако, если вам когда-нибудь удастся повторно использовать одноразовый номер, значит, вы облажались.Для такого небольшого количества байтов это означает, что вам в основном нужно хранить 4-байтовый счетчик, что означает необходимость хранить состояние .

Как правило, процедуры шифрования в режиме CTR требуют, чтобы вы предоставили IVа не одноразовый номер.Это просто начальное значение счетчика.Вы должны построить это значение, получив одноразовый номер, а затем с правым заполнением его нулевыми байтами, пока не получите 16 байтов, размер блока AES.

Вы можете найти образец кода здесь , не забудьте поднять голос.Роб также предоставил пример кода в своем ответе .

Шифрование с сохранением формата (FPE)

При шифровании с сохранением формата на выходе шифрования используется точное количество битили даже значения в качестве возможных значений ввода.Это звучит замечательно, но FPE состоит из нескольких относительно сложных алгоритмов.В основном вам нужно найти криптографическую библиотеку в JavaScript для ее реализации.

Обратите внимание, что в FPE одно и то же сообщение будет всегда шифроваться в один и тот же зашифрованный текст .В зависимости от приложения это может быть или не быть проблемой.


Примечания:

  • Вы используете шестнадцатеричное кодирование, это не самое плотное кодирование, вы можетеиспользуйте кодировку base 64 или даже ASCII 85.
  • Более эффективно сначала объединить IV и зашифрованный текст как байты , а затем выполнить кодирование.Если у вас статический размер для IV, вы можете просто использовать константу, а не разделитель.
  • Ваш ключ должен состоять из случайных байтов, а не текстовой строки.Пароли необходимо конвертировать с помощью хеширования паролей.
0 голосов
/ 12 мая 2018

Мартен покрывает большинство основных моментов, которые я хотел сделать, так что это всего лишь некоторая проработка и пример этого в Node.

Изменения в вашем коде:

  • Кодировать в Base64, а не в Hex. Это намного экономичнее. Было бы даже лучше просто использовать Buffers и вообще не создавать строку; тогда мы могли бы иметь 9-байтовый одноразовый номер, а не 5-байтовый одноразовый номер.
  • Избавиться от символа-разделителя между IV / nonce и зашифрованным текстом. Мы знаем, как долго IV / nonce; нам не нужен разделитель.
  • Используйте режим CTR, а не режим CBC. Это делает длину вывода равной длине ввода.
  • Используйте одноразовый номер, а не IV. Случайно выберите одноразовый номер из пространства 2 ^ 40. (Случайный выбор одноразового номера CTR в целом очень опасен. См. Ниже о том, почему может быть приемлемым в вашем случае использования; он по-прежнему никогда не рекомендуется.)
  • Исправлена ​​генерация пароля путем добавления PBKDF2 (вы также можете просто использовать randomBytes). Ваш пароль крайне ненадежен. Это строка ASCII, что означает, что она представляет крошечную часть пространства ключей AES-256. На порядок 0,000000000002% от пространства клавиш. Это намного менее безопасно, чем AES-256.

Как отмечает Мартен, в режиме CTR очень опасно дублировать пару Key + Nonce. Если кто-то делает это, он может узнать XOR двух оригинальных сообщений. При этом у них есть хороший шанс расшифровать оба сообщения. Например, если вы дублировали свой ключ + nonce на два сообщения, и злоумышленник использовал это, чтобы обнаружить, что их XOR равен 3, и знал, что зашифрованный текст был заглавной буквой, они знали бы, что эти два сообщения должны быть одним из них:

[('A', 'B'), ('D', 'G'), ('E', 'F'), ('H', 'K'), ('I', 'J'), ('L', 'O'),
 ('M', 'N'), ('P', 'S'), ('Q', 'R'), ('T', 'W'), ('U', 'V')]

Такая информация разрушительна для структурированных данных, таких как человеческий язык или компьютерные протоколы. Его можно очень быстро использовать для расшифровки всего сообщения. Использование ключа + nonce - это то, как WEP был сломан . (Когда вы делаете это вручную, это в основном идентично решению загадки криптограммы, которую вы найдете в газете.) Это менее мощно, чем более случайны зашифрованные данные, и тем меньше контекста они предоставляют.

При случайном 5-байтовом одноразовом числе вероятность столкновения составляет около 50% после 1,3 млн шифрования. При случайном 8-байтовом одноразовом коде вероятность коллизии составляет 50% после примерно 5,3 Б шифрования. sqrt (pi / 2 * 2 ^ бит)

В терминах криптографии это полностью нарушено. Это может или не может быть достаточно для ваших целей. Чтобы сделать это правильно (что я не делаю ниже), как отмечает Мартен, вы должны следить за своим счетчиком и увеличивать его для каждого шифрования, а не использовать случайное. После 2 ^ 40 шифрований (~ 1T) вы меняете свой ключ.

Предполагая, что утечка информации о двух сообщениях на миллион приемлема, именно так вы бы это реализовали.

const crypto = require('crypto');

const ENCRYPTION_KEY = 'Must256bytes(32characters)secret';
const SALT = 'somethingrandom';
const IV_LENGTH = 16;

const NONCE_LENGTH = 5; // Gives us 8-character Base64 output. The higher this number, the better

function encrypt(key, text) {
  let nonce = crypto.randomBytes(NONCE_LENGTH);
  let iv = Buffer.alloc(IV_LENGTH)
  nonce.copy(iv)

  let cipher = crypto.createCipheriv('aes-256-ctr', key, iv);
  let encrypted = cipher.update(text.toString());
  message = Buffer.concat([nonce, encrypted, cipher.final()]);
  return message.toString('base64')
}

function decrypt(key, text) {
  let message = Buffer.from(text, 'base64')
  let iv = Buffer.alloc(IV_LENGTH)
  message.copy(iv, 0, 0, NONCE_LENGTH)
  let encryptedText = message.slice(NONCE_LENGTH)
  let decipher = crypto.createDecipheriv('aes-256-ctr', key, iv);
  let decrypted = decipher.update(encryptedText);
  try{
    decrypted = Buffer.concat([decrypted, decipher.final()]);
    return decrypted.toString();
  }catch(Err){
    return 'NULL';
  }
}

// You could do this one time and record the result. Or you could just
// generate a random 32-byte key and record that. But you should never
// pass an ASCII string to the encryption function.
let key = crypto.pbkdf2Sync(ENCRYPTION_KEY, SALT, 10000, 32, 'sha512')

let encrypted = encrypt(key, "X")
console.log(encrypted + " : " + encrypted.length)

let decrypted = decrypt(key, encrypted)
console.log(decrypted)
0 голосов
/ 12 мая 2018

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

Ваш зашифрованный текст всегда имеет длину 66 символов (в шестнадцатеричном формате), поскольку вы храните 32-символьный IV, двухсимвольный разделитель («AP») и 32-значный (256-битный) зашифрованный текст. Ваш код использует заполнение, и поэтому зашифрованный текст всегда будет следующим кратным 16 байтам (32 шестнадцатеричным символам) по сравнению с длиной открытого текста, и поэтому вы получаете такую ​​длинную строку шестнадцатеричных символов для открытого текста. строка длиной всего 1 символ.

К сожалению для вас, для AES требуется заполнение (по крайней мере, в используемом вами режиме), иначе оно не будет работать.

...