2016-02-19 5 views
4

Я пишу систему, где пользователь может что-то написать (через мобильный браузер) и что «String» будет зашифрован с помощью пароля, выбранного пользователем. Поскольку unicode emojis часто используются, их также нужно поддерживать.AES шифрование с CryptoJS развращением unicode emoji

Как lib для криптографии, я выбираю CryptoJs - так что криптографию можно сделать локально на устройствах.

В настоящее время, когда я шифрую строку и расшифровываю то же самое, все emojis исчезают/заменяются случайными символами.

var key = "123"; 
var content = "secret text with an emoji, "; 

var encrypted = aes_encrypt(key, content); //U2FsdGVkX19IOHIt+eRkaOcmNuZrc1rkU7JepL4iNdUknzhDaLOnSjYBCklTktSe 

var decrypted = aes_decrypt(key, encrypted);//secret text with an emoji, Ø<ß® 

Я использую пару вспомогательных функций, как это:

function aes_encrypt(key, content){ 
    var key_string = key + ""; 
    var content_string = ascii_to_hex(content) + ""; 
    var key_sha3 = sha3(key_string); 
    var encrypted = CryptoJS.AES.encrypt(content_string, key_sha3, { 
     mode: CryptoJS.mode.CTR, padding: CryptoJS.pad.Iso10126}); 
    return encrypted + ""; 
}; 

Может кто-нибудь пожалуйста, скажите мне, что я делаю не так?

+2

Не могли бы вы предоставить ссылку на библиотеку криптографии, которую вы используете? Корневая проблема здесь заключается в том, что криптоалгоритмы работают с двоичными данными, а строки JavaScript - нет. Каждый символ в строке JavaScript имеет два байта. Криптокод, который обрабатывает строки JavaScript как двоичные данные, обычно игнорирует старший байт и предполагает, что нижние байты используются для хранения данных. Emoji требует, чтобы более высокий байт, данные которого теряются. Вам нужно явно кодировать ваши данные символа строки в UTF-8 в той или иной форме. Хакерным решением будет использование encode/decodeURIComponent до/после декодирования. –

+0

@JeremyBanks Я использую копию оригинальной библиотеки из кода google (https://code.google.com/archive/p/crypto-js/). –

+0

Является ли написанный вами файл aes_encrypt? – alexandergs

ответ

6

Предупреждение: Крайне сложно получить криптографический код вправо. Это может быть еще сложнее в JavaScript, где вам часто не хватает контроля над средой исполнения и (как обсуждается ниже) отсутствие поддержки языка привело к несовместимым соглашениям. Я недостаточно изучил библиотеку CryptoJS, чтобы узнать о ее дизайне или безопасности, или же он безопасно используется в этом контексте.

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

Общей проблемой при работе с криптографическим кодом в JavaScript является отсутствие встроенного способа представления двоичных данных. Это было разрешено в современных двигателях (с типами Blobs и TypedArrays в браузере и Buffers в Node.js), но по-прежнему существует много кода, который не использует это в силу исторических или соображений совместимости.

Без этих встроенных типов одно общее соглашение (используемое встроенными функциями atob и btoa) заключается в использовании встроенного строкового типа для хранения двоичных данных. Строка JavaScript представляет собой список двухбайтовых значений (обычно содержащих символы Unicode, кодированные UCS-2/UTF-16). Пользователи, которые хотят хранить двоичные данные, часто просто используют младший байт, полностью игнорируя высший байт.

Если вы используете только данные, совместимые с ASCII, вы можете уйти от игнорирования этих деталей при использовании кода, подобного этому (т. Е. Все будет работать, но могут быть незначительные последствия для безопасности). Это связано с тем, что текст, закодированный как ASCII, выглядит так же, как текст, закодированный как UTF-16, с вычеркнутыми высокими байтами. Но когда вы выходите за рамки этого, вам нужно сделать некоторую кодировку.

Наиболее правильной вещью (помимо использования реального двоичного типа) было бы взять входную строку символов, закодировать ее до UTF-8 и поместить эти данные в нижние байты выходной строки. Однако JavaScript не предоставляет встроенную функцию для этого. В качестве грубой, но простой альтернативы, the encodeURIComponent function будет кодировать любую действительную строку юникода в представление на основе UTF-8 полностью защищенных URL-символов, которые являются совместимыми с ASCII. В случае вашего кода, это будет означать что-то вроде этого:

var key = "123"; 
var content = "secret text with an emoji, "; 

var encrypted = aes_encrypt(key, encodeURIComponent(content)); 

var decrypted = decodeURIComponent(aes_decrypt(key, encrypted)); 

Если у вас есть много, не URL безопасных символов, это может привести к кодированным данным, будучи намного больше, чем необходимо, но оно должно быть безопасным.Кроме того, encodeURIComponent, по-видимому, выдает ошибку для строк, содержащих «непарные суррогатные символы». Я не думаю, что это должно происходить в обычном режиме, но кто-то может их изготовить.

Я ожидаю, что в CryptoJS есть более правильный способ справиться с такими вещами, но я не знаю об этом. Пожалуйста, подумайте над этим, если вы планируете развернуть этот код для общего использования.

+0

Это быстрый, грубый ответ, но я чувствовал, что лучше иметь что-то здесь, чем оставлять информацию, разбросанную по комментариям. Я не эксперт в области криптографии, это не надежный совет по криптографии, <вставьте еще десяток отклонений от ответственности здесь, обратитесь к эксперту> и т. Д. –

+0

a) Действительно нет необходимости в 'encodeURIComponent', потому что CryptoJS способен обрабатывать UTF -8. b) Увеличение размера пренебрежимо мало, так как OP уже удваивает размер с помощью 'ascii_to_hex()'. c) Вы правы, получить крипто право трудно. Я в основном даю текстовые описания (и ссылки) того, что нужно сделать, вместо того, чтобы показывать соответствующий код, потому что он взорвет длину сообщения. –

2

CryptoJS способен преобразовывать кодированную строку UTF-8 в собственный формат двоичных данных (WordArray). Это может быть достигнуто с var binData = CryptoJS.enc.Utf8.parse(string);:

var password = "123"; 
 
var content = "secret text with an emoji, "; 
 

 
inContent.innerHTML = content; 
 

 
var encrypted = aes_encrypt(password, content); 
 
var decrypted = aes_decrypt(password, encrypted); 
 

 
out.innerHTML = decrypted; 
 

 
function aes_encrypt(password, content) { 
 
    return CryptoJS.AES.encrypt(content, password).toString(); 
 
} 
 

 
function aes_decrypt(password, encrypted) { 
 
    return CryptoJS.AES.decrypt(encrypted, password).toString(CryptoJS.enc.Utf8); 
 
}
#inContent { color: blue; } 
 
#out { color: red; }
<script src="https://cdn.rawgit.com/CryptoStore/crypto-js/3.1.2/build/rollups/aes.js"></script> 
 
<div>in: <span id="inContent"></span></div> 
 
<div>out: <span id="out"></span></div>

Это работает, потому что, если строка передается как содержание в CryptoJS.AES.encrypt то он будет автоматически обрабатывается как UTF-8, но вам нужно преобразовать его обратно к UTF-8 после дешифрования самостоятельно. Это делается с помощью .toString(CryptoJS.enc.Utf8).


Этот код только демонстрирует, что CryptoJS обрабатывает UTF-8 уже очень хорошо. Это не является безопасным, поскольку

  • MD5 с одной итерацией используется для ввода ключа из пароля. Вам нужно будет использовать что-то вроде PBKDF2, которое предоставляет CryptoJS. (Не забудьте использовать случайный IV каждый раз. Это не обязательно должно быть секретным, поэтому вы можете отправить его вместе с зашифрованным текстом.)

  • Зашифрованный текст не аутентифицирован, что делает его маловероятным для обнаружения (злонамеренное) манипулирование зашифрованными данными. Лучше аутентифицировать ваши зашифрованные тексты, чтобы атаки, подобные padding oracle attack, не были возможны. Это можно сделать с помощью аутентифицированных режимов, таких как GCM или EAX, или с помощью схемы encrypt-then-MAC с сильным MAC, например HMAC-SHA256, который предоставляет CryptoJS.

+0

Спасибо за более обоснованное объяснение! –

Смежные вопросы