javascript 使用 CryptoJS 加密并使用 PHP 解密
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/29509934/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Encrypt with CryptoJS and decrypt with PHP
提问by Papauha
On the client side (mobile device) I encrypt a users password with CryptoJS:
在客户端(移动设备),我使用 CryptoJS 加密用户密码:
var lib_crypt = require('aes');
$.loginButton.addEventListener('click', function(e){
var key = lib_crypt.CryptoJS.enc.Hex.parse('bcb04b7e103a0cd8b54763051cef08bc55abe029fdebae5e1d417e2ffb2a00a3');
var iv = lib_crypt.CryptoJS.enc.Hex.parse('101112131415161718191a1b1c1d1e1f');
var encrypted = lib_crypt.CryptoJS.AES.encrypt($.passwordInput.value, key, { iv: iv });
var password_base64 = encrypted.ciphertext.toString(lib_crypt.CryptoJS.enc.Base64);
return password_base64;
});
On the server side i want to decrypt it with mcrypt_decrypt:
在服务器端,我想用 mcrypt_decrypt 解密它:
function decryptPassword($password)
{
$key = pack('H*', "bcb04b7e103a0cd8b54763051cef08bc55abe029fdebae5e1d417e2ffb2a00a3");
$ciphertext_dec = base64_decode($password);
$iv_dec = "101112131415161718191a1b1c1d1e1f";
$ciphertext_dec = substr($ciphertext_dec, 16);
$decryptedPassword = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $ciphertext_dec, MCRYPT_MODE_CBC, $iv_dec);
return trim($decryptedPassword);
}
I use the same key and IV, what do I do wrong?
我使用相同的密钥和IV,我做错了什么?
采纳答案by Artjom B.
You're not doing the same thing on both sides.
你不会在双方做同样的事情。
IV
四
You did parse the IV in CryptoJS, but forgot to do it in PHP:
您确实在 CryptoJS 中解析了 IV,但忘记在 PHP 中进行解析:
$iv_dec = pack('H*', "101112131415161718191a1b1c1d1e1f");
To fix that your IV is wrong, you probably noticed that the first 16 bytes are gibberish. That happens when the IV is wrong. Note that CryptoJS uses CBC mode by default, so the IV has only influence on the first block during decryption. Removethis:
要修复您的 IV 错误,您可能注意到前 16 个字节是乱码。当 IV 错误时就会发生这种情况。注意 CryptoJS 默认使用 CBC 模式,所以 IV 只影响解密时的第一个块。删除这个:
$ciphertext_dec = substr($ciphertext_dec, 16);
Padding
填充
You probably noticed that most plaintexts don't come out right. They end with some strange repeated characters at the end. This is the PKCS#7 padding that is applied by default in CryptoJS. You have to remove the padding yourself in PHP. Good thing is that Maarten Bodewes has provided a proper copy paste solution for this here.
您可能注意到大多数明文都没有正确输出。它们以一些奇怪的重复字符结尾。这是 CryptoJS 中默认应用的 PKCS#7 填充。您必须自己在 PHP 中删除填充。好消息是 Maarten Bodewes在这里为此提供了适当的复制粘贴解决方案。
trim()
might be appropriate for ZeroPadding, but not when a proper padding scheme like the one defined in PKCS#7 is used. You may remove the trim()
call altogether, because it is not useful and may result in unexpected plaintext, becauses zero bytes and whitespace is trimmed from the beginning and end.
trim()
可能适用于 ZeroPadding,但不适用于使用 PKCS#7 中定义的适当填充方案时。您可以trim()
完全删除调用,因为它没有用,并且可能会导致意外的纯文本,因为零字节和空格从头到尾都被修剪掉了。
回答by Danilo G. Veraszto
Hello,
你好,
in order to achieve this it should be considered to use the key and iv with 32 hex digits each, i had to solve exactly this doing my stuff and here is the way it goes
为了实现这一点,应该考虑使用 key 和 iv 各有 32 个十六进制数字,我必须解决这个问题,做我的事情,这是它的方式
<!--
This reach.your.crypto.js is just a ficticious placeholder,
that was used replaced by http://crypto-js.googlecode.com/svn/tags/3.1.2/build/,
which does not exist anymore,
which is the path to your CryptoJS library,
that can be downloaded through
https://code.google.com/archive/p/crypto-js/downloads?page=1
-->
<script src="reach.your.crypto.js/rollups/aes.js">
</script>
<script type="text/javascript">
//The key and iv should be 32 hex digits each, any hex digits you want,
//but it needs to be 32 on length each
var key = CryptoJS.enc.Hex.parse("0123456789abcdef0123456789abcdef");
var iv = CryptoJS.enc.Hex.parse("abcdef9876543210abcdef9876543210");
/*
if you wish to have a more friendly key, you can convert letters to Hex this way:
var a = "D";
var hex_D = a.charCodeAt(0).toString(16);
just to mention,
if it were to binary, it would be:
var binary_D = a.charCodeAt(0).toString(2);
*/
var secret = "Hi, this will be seen uncrypted later on";
//crypted
var encrypted = CryptoJS.AES.encrypt(secret, key, {iv:iv});
//and the ciphertext put to base64
encrypted = encrypted.ciphertext.toString(CryptoJS.enc.Base64);
//Assuming you have control on the server side, and know the key and iv hexes(we do),
//the encrypted var is all you need to pass through ajax,
//Let's follow with welcomed pure JS style, to reinforce one and other concept if needed
var xh = new XMLHttpRequest();
xh.open("POST", "decrypt_in_php.php", true);
xh.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xh.send("encrypted="+encodeURIComponent(encrypted));
</script>
And now receiving and decrypting in PHP
现在在 PHP 中接收和解密
<?php
//Here we have the key and iv which we know, because we have just chosen them on the JS,
//the pack acts just like the parse Hex from JS
$key = pack("H*", "0123456789abcdef0123456789abcdef");
$iv = pack("H*", "abcdef9876543210abcdef9876543210");
//Now we receive the encrypted from the post, we should decode it from base64,
$encrypted = base64_decode($_POST["encrypted"]);
$shown = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $encrypted, MCRYPT_MODE_CBC, $iv);
echo $shown;
//Although the decrypted is shown, there may be needed to trim and str_replace some \r \n \x06 \x05, if there is not a better "trim" way to do it though
?>
With this we will have back the "Hi, this will be seen uncrypted later on" :)
有了这个,我们将恢复“嗨,稍后会看到未加密”:)
回答by AymKdn
Here is a solution based on this comment, using openssl_decrypt
from PHP.
这是基于此注释的解决方案,使用openssl_decrypt
来自 PHP。
The JavaScriptpart (development with NodeJS for browsers) — first, install CryptoJSwith npm install crypto-js
, then your JS code:
在JavaScript的部分(与发展的的NodeJS浏览器) -第一,安装CryptoJS用npm install crypto-js
,那么你的JS代码:
import aes from 'crypto-js/aes'
import encHex from 'crypto-js/enc-hex'
import padZeroPadding from 'crypto-js/pad-zeropadding'
// message to encrypt
let msg = "Hello world";
// the key and iv should be 32 hex digits each, any hex digits you want, but it needs to be 32 on length each
let key = encHex.parse("0123456789abcdef0123456789abcdef");
let iv = encHex.parse("abcdef9876543210abcdef9876543210");
// encrypt the message
let encrypted = aes.encrypt(msg, key, {iv:iv, padding:padZeroPadding}).toString();
// and finally, send this "encrypted" string to your server
On the PHPside, your code will look like that:
在PHP方面,您的代码将如下所示:
// we use the same key and IV
$key = hex2bin("0123456789abcdef0123456789abcdef");
$iv = hex2bin("abcdef9876543210abcdef9876543210");
// we receive the encrypted string from the post
$encrypted = $_POST['decrypt'];
$decrypted = openssl_decrypt($encrypted, 'AES-128-CBC', $key, OPENSSL_ZERO_PADDING, $iv);
// finally we trim to get our original string
$decrypted = trim($decrypted);