Mcrypt js加密值与PHP生成的加密值不同mcrypt / Mcrypt JS解密对UTF-8字符不起作用

时间:2013-09-13 12:12:13

标签: php encryption mcrypt mcrypt-js

我一直在尝试在服务器端,PHP和客户端端实现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端解密。


*链接:*

Mcrypt JS library

Base64 JS library

2 个答案:

答案 0 :(得分:7)

主要问题似乎是您的string_encryptstring_decrypt PHP函数无权访问$key变量,因此加密密钥mcrypt_encrypt正在使用\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0。有关说明,请参阅this question。 PHP应报告key未定义的通知,您是否关闭了错误报告?从加密函数内部回显密钥以确认这一点。

另一个问题是Mcrypt JS库中的一个错误。如果密钥长度小于32字节,则此库使用\0填充加密密钥,问题是此 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发布的链接。
  4. <强>更新

    出现Base64编码问题是因为library you're using无法处理二进制数据。对于Base64 javascript库来说,这是一个相当普遍的问题。我建议改用this library

    对于使用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],
    

    为:

    "twofish"       :[  16,         32]
    

答案 1 :(得分:1)

我做的是:

<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>