Mcrypt js 加密值与 PHP 生成的不同 mcrypt/Mcrypt JS 解密不适用于 UTF-8 字符
我一直在尝试在服务器端、PHP 和客户端上实现 mcrypt 加密/解密技术.我正在尝试使用 mcrypt.js
库作为:
I have been trying to implement mcrypt encryption/ decryption technique on both server end, PHP and client end. I am trying to use mcrypt.js
library at the moment as:
<?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 库中使用了以下这些私有变量.
I have used these following private vars at the mcrypt.js library.
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)
?
As I commented above, why is it enc_str
not equal as $enc_str
and why is it mcrypt.Decrypt('<?php echo $enc_str ?>', '')
not equal as string_decrypt($enc_str, $key)
?
更新的问题:
我尝试了 base64 编码/解码甚至 hex2bin/bin2hex 来解析这些字符串,但这两个产生了以下结果:
I tried both base64 encode/ decode and even hex2bin/ bin2hex to parse those strings but these two produced following results:
使用 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 端解密.
And on both cases, UTf-8 content can not be decrypted on JS end.
*链接:*
Mcrypt JS 库
Base64 JS 库
推荐答案
主要问题似乎是您的 string_encrypt
和 string_decrypt
PHP 函数无法访问$key
变量,因此对于加密密钥 mcrypt_encrypt
使用 .有关解释,请参阅这个问题.PHP 应该报告
key
未定义的通知,您是否关闭了错误报告?从加密函数内部回显密钥以确认这一点.
The main issue appears to be that your string_encrypt
and string_decrypt
PHP functions don't have access to the $key
variable, so for the encryption key mcrypt_encrypt
is using . See this question for an explanation. PHP should report a notice that
key
is undefined, have you turned off error reporting perhaps? Echo the key from inside the encrypt function to confirm this.
另一个问题是 Mcrypt JS 库中的一个错误.这个库用 填充加密密钥,如果密钥长度小于 32 字节,问题是这个不是PHP
mcrypt_encrypt
功能键.mcrypt_encrypt
函数将密钥填充到最接近的有效密钥长度(16、24 或 32 字节).mcrypt.js 中的问题在第 63 和 64 行,更改此:
Another issue is a bug in the Mcrypt JS library. This library pads the encryption key with if the key length is less than 32 bytes, the problem is that this is not how the PHP
mcrypt_encrypt
function pads the key. The mcrypt_encrypt
function pads the key up to the nearest valid key length (16, 24, or 32 bytes). The issue in mcrypt.js is at lines 63 and 64, change this:
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));
现在我们可以确认修复...
Now we can confirm the fix...
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:
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,然后在尝试解密之前对其进行解码.
Finally, all of these encryption functions produce binary as their output. Binary cannot be written as plain text in most cases without damaging the data. To solve this, either encode the binary to Hex or Base64 and then decode it before trying to decrypt.
所以为了让一切正常...
So to get everything working...
<?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>
还有一些注意事项...
A few more notes...
- 您不能
修剪
string_encrypt
函数的输出.这将导致删除前导或尾随零,从而使您无法解密输出. - ECB 模式不安全,您真的不应该使用它.CBC是要走的路.CBC 确实需要一个 IV,而且加密和解密的 IV 必须相同.
- 由于各种原因,Javascript 加密并不安全,考虑到您对它的使用,任何人都可以简单地查看页面源代码或调试正在运行的 javascript 以获取加密密钥.阅读 ntoskrnl 在您的问题评论中发布的链接.
- You cannot
trim
the output of thestring_encrypt
function. This will cause leading or trailing zeros to be removed, which will make it so that you cannot decrypt the output. - ECB mode is insecure and you really shouldn't use it. CBC is the way to go. CBC does require an IV, and the IV must be the same for both encryption and decryption.
- Javascript encryption is not secure for various reasons, given your usage of it anyone could simply view the pages source or debug the running javascript to get the encryption key. Read the link posted by ntoskrnl in your question comments.
更新:
出现您的 Base64 编码问题是因为您使用的库不能处理二进制数据.这是 Base64 javascript 库的一个相当普遍的问题.我建议使用 这个库 代替.
Your Base64 encoding issue occurs because the library you're using doesn't work with binary data. This is a fairly common issue for Base64 javascript libraries. I'd recommend using this library instead.
至于使用javascript解密时的尾随
字符,您需要修剪解密输出.您是在 PHP string_decrypt
方法中执行此操作,而不是在您的 javascript 中执行此操作.您可以通过对字符串末尾的所有 字符执行正则表达式替换来修剪解密的输出.
As for the trailing �
characters when decrypting with javascript, you need to trim the decrypted output. You're doing this in your PHP string_decrypt
method, but not in your javascript. You can trim the decrypted output by doing a regex replace on all characters at the end of the string.
示例:
mcrypt.Decrypt(dec_str,'').replace(/x00+$/g, '')
我应该在我的原始帖子中包含这个,但是我没有注意到输出中的 字符,因为 FF 的警告框没有显示它们.很抱歉.
I should have included this in my original post, but I didn't notice the characters in the output because FF's alert box doesn't display them. Sorry about that.
最后,我注意到 Mcrypt JS 库中的另一个错误.第 41 至 47 行:
Finally, I noticed another bug in the Mcrypt JS library. Lines 41 to 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 库.要解决问题更改:
Notice the comma at the end of the "twofish" line. Firefox and Chrome don't seem to mind this, but IE8 will report an error and fail to load the mcrypt library because of it. To fix the issue change:
"twofish" :[ 16, 32],
到:
"twofish" :[ 16, 32]
相关文章