简体   繁体   English

使用 CryptoJS 加密并使用 PHP 解密

[英]Encrypt with CryptoJS and decrypt with PHP

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,我做错了什么?

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" :)有了这个,我们将恢复“嗨,稍后会看到未加密”:)

Here is a solution based on this comment , using openssl_decrypt from PHP.这是基于此评论的解决方案,使用 PHP 中的openssl_decrypt

The JavaScript part (development with NodeJS for browsers) — first, install CryptoJS with npm install crypto-js , then your JS code: JavaScript部分(使用 NodeJS 为浏览器开发)——首先,使用npm install crypto-js安装CryptoJS ,然后是你的 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 PHP side, 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);

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.要修复您的 IV 错误,您可能注意到前 16 个字节是乱码。 That happens when the IV is wrong.当 IV 错误时就会发生这种情况。 Note that CryptoJS uses CBC mode by default, so the IV has only influence on the first block during decryption.注意 CryptoJS 默认使用 CBC 模式,所以 IV 只影响解密时的第一个块。 Remove this:删除这个:

$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.这是 CryptoJS 中默认应用的 PKCS#7 填充。 You have to remove the padding yourself in PHP.您必须自己在 PHP 中删除填充。 Good thing is that Maarten Bodewes has provided a proper copy paste solution for this here .好消息是 Maarten Bodewes在这里为此提供了适当的复制粘贴解决方案。

trim() might be appropriate for ZeroPadding, but not when a proper padding scheme like the one defined in PKCS#7 is used. trim()可能适用于 ZeroPadding,但不适用于使用 PKCS#7 中定义的适当填充方案时。 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()调用,因为它没有用,并且可能会导致意外的明文,因为零字节和空格从头到尾都被修剪掉了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM