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

hsu*_*suk 6 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>
Run Code Online (Sandbox Code Playgroud)

我在mcrypt.js库中使用了以下私有变量.

 var cMode='ecb';
 var cCipher='rijndael-256';
 var cKey='testtesttesttesttesttesttesttest'; 
 //I am providing the same key
Run Code Online (Sandbox Code Playgroud)

正如我在上面评论的那样,为什么它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.
Run Code Online (Sandbox Code Playgroud)

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.*/
Run Code Online (Sandbox Code Playgroud)

使用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.
Run Code Online (Sandbox Code Playgroud)

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éÓ!ÆÖ
Run Code Online (Sandbox Code Playgroud)

在这两种情况下,UTf-8内容都无法在JS端解密.


*链接:*

Mcrypt JS库

Base64 JS库

Syo*_*yon 7

主要问题似乎是您string_encryptstring_decryptPHP函数无法访问$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));
Run Code Online (Sandbox Code Playgroud)

对此:

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));
Run Code Online (Sandbox Code Playgroud)

现在我们可以确认修复...

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
Run Code Online (Sandbox Code Playgroud)

使用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
Run Code Online (Sandbox Code Playgroud)

最后,所有这些加密函数都产生二进制作为其输出.在大多数情况下,二进制文件不能以纯文本形式写入而不会损坏数据.要解决此问题,请将二进制编码为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>
Run Code Online (Sandbox Code Playgroud)

还有一些笔记......

  1. 你不能trim输出这个string_encrypt功能.这将导致删除前导零或尾随零,这将使您无法解密输出.
  2. ECB模式不安全,你真的不应该使用它.CBC是要走的路.CBC 确实需要IV,并且加密和解密的IV必须相同.
  3. Javascript加密由于各种原因而不安全,因为您使用它可以只查看页面源或调试运行的javascript来获取加密密钥.在您的问题评论中阅读ntoskrnl发布的链接.

更新:

出现Base64编码问题是因为您使用不能使用二进制数据.对于Base64 javascript库来说,这是一个相当普遍的问题.我建议改用这个库.

对于?使用javascript解密时的尾随字符,您需要修剪解密输出.你在PHP string_decrypt方法中这样做,但不是在你的javascript中.您可以通过\0对字符串末尾的所有字符执行正则表达式替换来修剪解密输出.

例:

mcrypt.Decrypt(dec_str,'').replace(/\x00+$/g, '')
Run Code Online (Sandbox Code Playgroud)

我应该把它包含在我的原始帖子中,但是我没有注意到\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],
}
Run Code Online (Sandbox Code Playgroud)

注意"twofish"行末尾的逗号.Firefox和Chrome似乎并不介意,但IE8会报告错误,因此无法加载mcrypt库.要解决问题更改:

"twofish"       :[  16,         32],
Run Code Online (Sandbox Code Playgroud)

至:

"twofish"       :[  16,         32]
Run Code Online (Sandbox Code Playgroud)