【问题标题】:Encrypt with CryptoJS and decrypt with PHP用 CryptoJS 加密,用 PHP 解密
【发布时间】:2015-06-13 03:11:11
【问题描述】:

在客户端(移动设备)我使用 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; 
});

在服务器端我想用 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);
}

我使用相同的密钥和IV,我做错了什么?

【问题讨论】:

  • 您可能想要添加您遇到的问题的示例。从代码来看,我目前只是假设我的答案。
  • 感谢 Artjom 的快速响应,删除填充用什么更好:Maarten Bodewes 或 trim() 的解决方案?谢谢
  • trim() 仅在 16 次中有效。永远不要trim(),因为如果明文在开头或结尾包含零字节或空格,这可能会导致无法预料的后果。

标签: javascript php encryption mcrypt cryptojs


【解决方案1】:

双方做的事情不一样。

IV

您确实在 CryptoJS 中解析了 IV,但忘记在 PHP 中解析:

$iv_dec = pack('H*', "101112131415161718191a1b1c1d1e1f");

要解决您的 IV 错误,您可能注意到前 16 个字节是乱码。当 IV 错误时,就会发生这种情况。请注意,CryptoJS 默认使用 CBC 模式,因此 IV 在解密过程中仅对第一个块有影响。 删除这个:

$ciphertext_dec = substr($ciphertext_dec, 16);

填充

您可能注意到大多数纯文本的输出都不正确。它们以一些奇怪的重复字符结尾。这是 CryptoJS 中默认应用的 PKCS#7 填充。您必须自己在 PHP 中删除填充。好消息是 Maarten Bodewes 为这个 here 提供了适当的复制粘贴解决方案。

trim() 可能适用于 ZeroPadding,但不适用于使用 PKCS#7 中定义的适当填充方案时。您可以完全删除 trim() 调用,因为它没有用,并且可能导致意外的纯文本,因为零字节和空白从开头和结尾被修剪。

【讨论】:

    【解决方案2】:

    你好,

    为了实现这一点,应该考虑使用 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>
    

    现在在 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
    ?>
    

    有了这个,我们将返回“嗨,稍后会看到它是未加密的” :)

    【讨论】:

    【解决方案3】:

    这是一个基于this comment 的解决方案,使用PHP 中的openssl_decrypt

    JavaScript 部分(使用 NodeJS 开发浏览器)——首先,安装 CryptoJSnpm 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
    

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

    【讨论】:

    • 如果我们也必须在 php 端获得相同的加密文本,如何做 openssl_encrypt。
    • 什么对我来说很重要 -> 密码的长度。我尝试使用自定义密码,只是像“xyz”这样的字符串,但它不起作用。它必须像本例中那样进行密码编码。谢谢
    • jsfiddle 测试示例:jsfiddle.net/5bs2rndu/5
    猜你喜欢
    • 2015-08-27
    • 2015-10-17
    • 1970-01-01
    • 2014-08-17
    • 2014-12-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-01
    相关资源
    最近更新 更多