Значение шифрования Mcrypt js отличается от значения, полученного с помощью PHP mcrypt / Mcrypt. Расшифровка JS не работает для символов UTF-8.

Я пытался внедрить метод шифрования/дешифрования mcrypt как на стороне сервера, так и на стороне клиента. Я пытаюсь использовать библиотеку mcrypt.js на данный момент как:

<?php 
$key = 'testtesttesttesttesttesttesttest';

function string_encrypt($string, $key) {
    $crypted_text = mcrypt_encrypt(
                        MCRYPT_RIJNDAEL_128, 
                        $key, 
                        $string, 
                        MCRYPT_MODE_ECB
                    );
    return base64_encode($crypted_text);
}

function string_decrypt($encrypted_string, $key) {
    $decrypted_text = mcrypt_decrypt(
                        MCRYPT_RIJNDAEL_128, 
                        $key, 
                        base64_decode($encrypted_string), 
                        MCRYPT_MODE_ECB
                    );
    return trim($decrypted_text);
}

echo 'Provided Text:    '.$test_str = 'This is test message.';
echo '<br />';
echo 'Encyrpted Value:  '.$enc_str = string_encrypt($test_str, $key);   
echo '<br />';
echo 'Decrypted Value:  '.string_decrypt($enc_str, $key);                               
echo '<br />';
?>

<script src='rijndael.js'></script>
<script src='mcrypt.js'></script>
<script src='base64v1_0.js'></script>

<script lang='javascript'>
    var enc_str = mcrypt.Encrypt('<?php echo $test_str ?>','');
    enc_str = B64.encode(enc_str);
    alert(enc_str); 
    // I don't get this same as encypted PHP text. i.e. $enc_str
    var dec_str = B64.decode('<?php echo $enc_str ?>');
    alert(mcrypt.Decrypt(dec_str,'')); 
    // I don't get this same as decypted PHP text. 
    // i.e. string_decrypt($enc_str)
</script>

Я использовал следующие частные переменные в библиотеке mcrypt.js.

 var cMode='ecb';
 var cCipher='rijndael-256';
 var cKey='testtesttesttesttesttesttesttest'; 
 //I am providing the same key

Как я уже говорил выше, почему enc_str не равно $enc_str и почему mcrypt.Decrypt('<?php echo $enc_str ?>', '') не равно string_decrypt($enc_str, $key)?



Обновленный вопрос:

Я пробовал кодировать/декодировать base64 и даже hex2bin/bin2hex для анализа этих строк, но эти два дали следующие результаты:


Использование Hex2bin/ Bin2hex

Результат PHP:

Provided Text: This is test message.
Encyrpted Value: a51e970427ec8f666a5684cc1712ad03b29889cc10f4ccbf55733564d11c0386
Decrypted Value: This is test message.

Результат JS:

Provided Text:This is test message.
Mcrypted value:¥'ìfjVÌ­²ÌôÌ¿Us5dÑ
Encyrpted Value:a51e970427ec8f666a5684cc1712ad03b29889cc10f4ccbf55733564d11c0386
After Hex to Bin Text:¥'ìfjVÌ­²ÌôÌ¿Us5dÑ
Decrypted Value:This is test message.�����������
/*These diamond with question mark is produced while decypting the value.*/

Использование кодирования/декодирования Base64:

Результат PHP:

Provided Text: This is test message.
Mcrypt encrypted value : ¥—'ìfjV„Ì­²˜‰ÌôÌ¿Us5dц
/*
 Here mcrypted value provided by JS and PHP is different
 That is causing to produce different value at two ends
*/
Encyrpted Value: pR6XBCfsj2ZqVoTMFxKtA7KYicwQ9My/VXM1ZNEcA4Y=
Decrypted Value: This is test message.

Результат JS:

Provided Text:This is test message.
Mcrypted value:¥'ìfjVÌ­²ÌôÌ¿Us5dÑ
Encyrpted Value:wqUewpcEJ8Oswo9malbChMOMFxLCrQPCssKYwonDjBDDtMOMwr9VczVkw5EcA8KG
After Base64 Decode:¥'ìfjVÌ­²ÌôÌ¿Us5dÑ���
Decrypted Value:This is test message.�����������bFaêF«+JéÓ!ÆÖ

И в обоих случаях содержимое UTf-8 не может быть расшифровано на стороне JS.


*Ссылки: *

JS-библиотека Mcrypt

JS-библиотека Base64


person Community    schedule 13.09.2013    source источник
comment
Работает ли он по-другому, если вы передаете свой режим/шифр/ключ непосредственно в функции javascript encrypt() и decrypt()? Есть вероятность, что вы просто допустили ошибку при добавлении их в mcrypt.js?   -  person Adrian Wragg    schedule 13.09.2013
comment
Вы хотите сказать, что mcrypt.Encrypt('<?php echo $test_str ?>', iv) дает результат, отличный от mcrypt.Encrypt('<?php echo $test_str ?>', iv, 'testtesttesttesttesttesttesttest', 'rijndael-256', 'ecb');?   -  person Adrian Wragg    schedule 13.09.2013
comment
Похоже, IV игнорируется для режима ECB, так что здесь это не имеет значения.   -  person Adrian Wragg    schedule 13.09.2013
comment
См. эту статью: matasano.com/articles/javascript-cryptography.   -  person ntoskrnl    schedule 13.09.2013


Ответы (2)


Основная проблема заключается в том, что ваши PHP-функции string_encrypt и string_decrypt не имеют доступа к переменной $key, поэтому для ключа шифрования mcrypt_encrypt используется \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0. Смотрите этот вопрос для объяснения. PHP должен сообщать об уведомлении о том, что key не определено, возможно, вы отключили отчет об ошибках? Повторите ключ внутри функции шифрования, чтобы подтвердить это.

Еще одна проблема — ошибка в библиотеке Mcrypt JS. Эта библиотека дополняет ключ шифрования цифрой \0, если длина ключа меньше 32 байтов. Проблема в том, что это не так, как функция PHP mcrypt_encrypt дополняет ключ. Функция mcrypt_encrypt дополняет ключ до ближайшей допустимой длины ключа (16, 24 или 32 байта). Проблема в mcrypt.js находится в строках 63 и 64, измените это:

if(key.length<32)
    key+=Array(33-key.length).join(String.fromCharCode(0));

к этому:

if(key.length<16)
    key+=Array(17-key.length).join(String.fromCharCode(0));
else if(key.length<24 && key.length>16)
    key+=Array(25-key.length).join(String.fromCharCode(0));
else if(key.length<32 && key.length>24)
    key+=Array(33-key.length).join(String.fromCharCode(0));

Теперь мы можем подтвердить исправление...

PHP:

function string_encrypt($string) {
    $crypted_text = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, "", $string, MCRYPT_MODE_ECB);
    return $crypted_text;
}

$test_str = "This is test message to be encrypted.";
$enc_str = string_encrypt($test_str);
echo bin2hex($enc_str);

Output:
f98fca4ddc4c10d6cd47df56b081b78566ee4facbcf2254b46f7809d9b255529d2078f28b150e802d72818be1888536fac6219f6ce7b9d9332a24afa09288f0e

Javascript:

function toHex(str) {
    var hex = '';
    for(var i=0;i<str.length;i++) {
        var val = ''+str.charCodeAt(i).toString(16);
        if(val.length == 1)
            hex += '0'+val;
        else
            hex += val;
    }
    return hex;
}

var enc_str = mcrypt.Encrypt("This is test message to be encrypted.", "", "", "rijndael-256", "ecb");
alert(toHex(enc_str));

Output:
f98fca4ddc4c10d6cd47df56b081b78566ee4facbcf2254b46f7809d9b255529d2078f28b150e802d72818be1888536fac6219f6ce7b9d9332a24afa09288f0e

Наконец, все эти функции шифрования выдают на выходе двоичный код. В большинстве случаев двоичный файл нельзя записать как обычный текст без повреждения данных. Чтобы решить эту проблему, либо закодируйте двоичный файл в Hex или Base64, а затем декодируйте его, прежде чем пытаться расшифровать.

Итак, чтобы все работало...

<?php 
$key = 'testtesttesttesttesttesttesttest';

function string_encrypt($string, $key) {
    $crypted_text = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $string, MCRYPT_MODE_ECB);
    return $crypted_text;
}

function string_decrypt($encrypted_string, $key) {
    $decrypted_text = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $encrypted_string, MCRYPT_MODE_ECB);
    return trim($decrypted_text);
}

echo $test_str = 'This is test message to be encrypted.';   echo '<br />';
$enc_str = string_encrypt($test_str, $key);
echo bin2hex($enc_str);                                     echo '<br />';
echo string_decrypt($enc_str, $key);                        echo '<br />';

?>

<script src='rijndael.js'></script>
<script src='mcrypt.js'></script>

<script lang='javascript'>
    function toHex(str) {
        var hex = '';
        for(var i=0;i<str.length;i++) {
            var val = ''+str.charCodeAt(i).toString(16);
            if(val.length == 1)
                hex += '0'+val;
            else
                hex += val;
        }
        return hex;
    }
    function hexToString (hex) {
        var str = '';
        for (var i=0; i<hex.length; i+=2) {
            str += ''+String.fromCharCode(parseInt(hex.charAt(i)+hex.charAt(i+1), 16));
        }
        return str;
    }
    var enc_str = mcrypt.Encrypt('<?php echo $test_str ?>', '', 'testtesttesttesttesttesttesttest', 'rijndael-256', 'ecb');
    alert(toHex(enc_str));
    alert(mcrypt.Decrypt(hexToString('<?php echo bin2Hex($enc_str) ?>'), '', 'testtesttesttesttesttesttesttest', 'rijndael-256', 'ecb').replace(/\x00+$/g, '')); 
</script>

Еще несколько замечаний...

  1. Вы не можете trim вывести функцию string_encrypt. Это приведет к удалению начальных или конечных нулей, что сделает так, что вы не сможете расшифровать вывод.
  2. Режим ECB небезопасен, и вам действительно не следует его использовать. CBC - это путь. CBC требует IV, и IV должен быть одинаковым как для шифрования, так и для дешифрования.
  3. Шифрование Javascript не является безопасным по разным причинам, учитывая его использование, любой может просто просмотреть исходный код страниц или отладить работающий javascript, чтобы получить ключ шифрования. Прочтите ссылку, опубликованную ntoskrnl в комментариях к вашему вопросу.

Обновление:

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

Что касается завершающих символов при расшифровке с помощью javascript, вам необходимо обрезать расшифрованный вывод. Вы делаете это в своем методе PHP string_decrypt, но не в своем javascript. Вы можете обрезать расшифрованный вывод, выполнив замену регулярным выражением всех символов \0 в конце строки.

Пример:

mcrypt.Decrypt(dec_str,'').replace(/\x00+$/g, '')

Я должен был включить это в свой исходный пост, но я не заметил символов \0 в выводе, потому что окно предупреждения FF их не отображает. Извини за это.

Наконец, я заметил еще одну ошибку в библиотеке Mcrypt JS. Строки с 41 по 47:

var ciphers={       //  block size, key size
    "rijndael-128"  :[  16,         32],
    "rijndael-192"  :[  24,         32],
    "rijndael-256"  :[  32,         32],
    "serpent"       :[  16,         32],
    "twofish"       :[  16,         32],
}

Обратите внимание на запятую в конце строки «twofish». Firefox и Chrome, кажется, не возражают против этого, но IE8 сообщит об ошибке и из-за этого не сможет загрузить библиотеку mcrypt. Чтобы устранить проблему, выполните следующие действия:

"twofish"       :[  16,         32],

to:

"twofish"       :[  16,         32]
person Syon    schedule 16.09.2013
comment
@hsuk: обновлено, исправлена ​​проблема с размещением необработанного зашифрованного текста непосредственно в источнике страницы. PHP теперь печатает шестнадцатеричный код внутри вызова метода mcrypt.Decrypt, который декодируется с помощью javascript. - person Syon; 16.09.2013
comment
Большое спасибо за ваши усилия, передача $key в качестве параметра решила мою проблему, но у меня есть другие проблемы. Пожалуйста, смотрите обновленный вопрос. - person hsuk; 17.09.2013
comment
Еще раз большое спасибо ... Теперь я мог бы получить одно и то же зашифрованное значение на обоих концах, то есть после кодирования enryption + base64, но оно все еще отличается для символов Unicode ... - person hsuk; 18.09.2013
comment

Что я сделал:

<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<?php 
$key = '12345678911234567892123456789312';

function string_encrypt($string, $key) {
    $crypted_text = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $string, MCRYPT_MODE_ECB);
    return base64_encode($crypted_text);
}

function string_decrypt($encrypted_string, $key) {
    return mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, base64_decode($encrypted_string), MCRYPT_MODE_ECB);
}

echo 'Provided Text:    <br />' . $test_str =  'this is test काठमाडौं - राष्ट्रिय सुरक्षा परिषद्ले आसन्न संविधानसभा चुनावको सुरक्षामा नेपाली सेना खटाउने निर्णय गरेको छ। मंगलबार साँझ बसेको परिषद् बैठकले सुरक्षा अवस्था संवेदनशील भएको कारण देखाउँदै सात वर्षपछि सेना परिचालनका लागि मन्त्रिपरिषदसमक्ष सिफारिस गर्ने निर्णय गरेको हो। this is test';                           
echo '<br />';echo '<br />';

echo 'Encrypted Value:  <br />' . $enc_str = string_encrypt($test_str, $key);
echo '<br />';echo '<br />';

echo 'Decrypted Value:  <br />' . string_decrypt($enc_str, $key);
echo '<br />';echo '<br />';
?>
<script src='stringcoders.base64.js'></script>
<script src='rijndael.js'></script>
<script src='mcrypt.js'></script>

<script lang='javascript'  charset="utf-8">
    var is_IE = /*@cc_on!@*/false;
    var message = '';

    var str = '<?php echo $test_str ?>';
    (is_IE) ? (message+= 'Provided Text:' + str) : console.log('Provided Text: \n' + str);
    str = unescape(encodeURIComponent(str));

    /* 
        I added this. Converted to ISO Latin before encryting and vice versa on decryption.
        Though don't know how this made work.
    */  
    var enc_str = mcrypt.Encrypt(str, '');
    enc_str = base64.encode(enc_str);
    (is_IE) ? (message+= 'Encrypted Value:' + enc_str) : console.log('\nEncyrpted Value:\n' + enc_str);

    var dec_str = base64.decode(enc_str);
    dec_str = mcrypt.Decrypt(dec_str, '').replace(/\x00+$/g, '');
    dec_str = decodeURIComponent(escape(dec_str));
    (is_IE) ? (message+= 'Decrypted Value:' + dec_str) : console.log('\nDecrypted Value:\n' + dec_str); 

    (is_IE) ? alert(message) : '';
</script>
person Community    schedule 18.09.2013