【问题标题】:Javascript DES encryption/PHP decryptionJavascript DES加密/PHP解密
【发布时间】:2014-02-26 13:00:57
【问题描述】:

我有一个供多个网站使用的 PHP 端点。在将数据发布到端点时,站点使用加密的标头来识别各种信息。网站使用标准的 PHP mcrypt_ecb 函数,端点解密变量如下:

$var = trim(mcrypt_ecb(MCRYPT_DES, $key, base64_decode($input), MCRYPT_DECRYPT)));

我真的没有太多机会改变 PHP 端点,尽管如果mcrypt_ecb 不能很好地发挥作用,我可以想象制作另一个端点。

我现在有一个 PhoneGap 应用程序,它还需要发送加密的标头。 Phonegap 只能真正使用 javascript 来 POST 到远程服务器。我查看了 Google CryptoJS 以尝试实现这一点,以及一个 base64 库。请注意,密钥与上面的 $key var 相同。

var encrypted = CryptoJS.DES.encrypt(user.email, key).toString();
var base64 = base64.encode(encrypted);

问题是通过mcrypt_ecb 传递的加密字符串(我尝试过不使用base64 编码/解码)是垃圾。显然有不同的三重DES加密方法?

如何在 JavaScript 中创建mcrypt_ecb 兼容的加密字符串?

【问题讨论】:

  • 请将您的解决方案作为答案发布,然后接受。谢谢。
  • 未来读者:1. DES 已被弃用且不安全,已被 AES 取代,请勿在新作品中使用。 2.新作品不要使用ECB模式,尽快更新旧作品,不安全,见ECB mode,向下滚动到企鹅。而是使用带有随机IV的CBC模式,只需在加密数据前加上IV用于解密即可,它不需要保密。
  • 未来读者:最好不要使用 PHP mcrypt,它是废弃软件,多年未更新,不支持标准 PKCS#7(née PKCS#5)填充,只有非标准甚至不能用于二进制数据的空填充。 mcrypt 有许多出色的 bugs 可以追溯到 2003 年。不推荐使用的 mcrypt 扩展将在 PHP 7.2 中删除。而是考虑使用defuseRNCryptor,它们提供了一个完整的解决方案并且正在维护并且是正确的。

标签: javascript php encryption


【解决方案1】:

根据: http://php.net/manual/en/function.mcrypt-decrypt.php

警告 自 PHP 7.1.0 起,此函数 (mcrypt-decrypt) 已被弃用。强烈建议不要依赖此函数。

然后我不得不进行大量调查,直到找到一种通过 javascript 使用 openssl 加密并使用 php 解密的方法。

我遇到的唯一问题是发送长文本时。这是因为 RSA,根据定义,支持长度有限的字符串。

https://security.stackexchange.com/questions/33434/rsa-maximum-bytes-to-encrypt-comparison-to-aes-in-terms-of-security/33445#33445

按照 PKCS#1 的定义,RSA 对大小有限的“消息”进行加密。和 常用的“v1.5 padding”和一个 2048 位的 RSA 密钥,最大 可以使用 RSA 加密的数据大小为 245 字节。没有了。

即 如果我使用 1024 的 private_key_bits 我可以发送

"José compró en Perú una vieja zampoña. Excusándose, Sofía tiró su whisky al desagüe de la banqueta."

没有了。 如果我使用 512 的 private_key_bits 我可以发送

"José compró en Perú una vieja zampoña. Excusánd"

没有了。

关于长字符串 JavaScript 控制台报告:“对于 RSA 而言消息太长”

然后如果你想加密长字符串你必须在javascript加密之前和解密之后压缩和拆分它们在php上加入和解压缩,我认为zlib是split/join的一个很好的解决方案,因为它在javascript和php上都支持。

也许使用 AES 库而不是像 https://github.com/brainfoolong/cryptojs-aes-php 这样的库更好

我的工作代码如下:

<?php
    //------------------------------------------------------------
    // Global Settings.
    //------------------------------------------------------------
    ini_set('display_errors', 1);
    error_reporting(E_ALL);
    $directorio = "/path/to/key/directory/apache/writable/";
    $nombre_base = "llaves_php";

    //------------------------------------------------------------
    // Initialization.
    //------------------------------------------------------------
    $encabezado_html = "";
    $cuerpo_html = "";

    //------------------------------------------------------------
    // Loading keys
    //------------------------------------------------------------
    list($privateKey, $pubKey) =
        cargar_llaves_RSA($directorio, $nombre_base);

    //------------------------------------------------------------
    // Form that uses javascript to encrypt data.
    // (it uses only the public key)
    //------------------------------------------------------------
    $librerias_html = "
        <script type='text/javascript'
                src='https://ajax.googleapis.com/ajax/libs/".
                    "jquery/3.2.1/jquery.min.js'></script>
        <script type='text/javascript'
                src='lib/jsencrypt.js'></script>
        ";

    $pubKey_html = htmlentities($pubKey);
    $datos_html = "
        <h2>Cifrando con Javascript</h2>
        <input type='text' id='mensaje' />
        <br />
        <button id='ENVIAR'>Enviar</button>
        <br />
        <textarea id='pubkey' style='display: none;'>".
        $pubKey_html.
        "</textarea>
        <script type='text/javascript'>
            $('#ENVIAR').click(function () {
                var codificador = new JSEncrypt();
                codificador.setKey($('#pubkey').val());
                var cifrado = codificador.encrypt($('#mensaje').val());
                window.open('?mensaje=' + encodeURIComponent(cifrado)
                           , '_top');
            });
        </script>
        ";

    //------------------------------------------------------------
    // Decrypting using php (it uses only the privateKey)
    //------------------------------------------------------------
    if (isset($_REQUEST['mensaje'])) {
        openssl_private_decrypt( base64_decode($_REQUEST['mensaje'])
                               , $descifrado
                               , $privateKey);
        $datos_html.= "
            <h2>Descifrando con PHP</h2>
            ".$descifrado."
            ";
    }

    //------------------------------------------------------------
    // HTML DISPLAY
    //------------------------------------------------------------
    $encabezado_html.= "<title>Receptor de mensaje cifrado</title>"
                     . $librerias_html;

    $cuerpo_html.= $datos_html;

    $contenido = "<head>$encabezado_html</head><body>$cuerpo_html</body>";
    $contenido = "<html>$contenido</html>";
    print $contenido;

//============================================================
//============================================================
// Functions
//============================================================
//============================================================

    //------------------------------------------------------------
    function cargar_llaves_RSA($directorio, $nombre_base) {
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // PROPÓSITO: Genera o carga desde archivos las llaves RSA
    // ENTRADAS:
    // $directorio: Directorio donde se encuentran los archivos.
    // $nombre_base: Nombre, sin extensión, de los archivos con
    //               las llaves.
    // SALIDAS:
    //------------------------------------------------------------
        if (  !file_exists($directorio.$nombre_base.".crt")
           || !file_exists($directorio.$nombre_base.".pub")) {
            list($privateKey, $pubKey) = crear_llaves_RSA($directorio.$nombre_base);
        } else {
            //------------------------------------------------------------
            // CARGA DE LLAVES RSA ARCHIVADAS
            //------------------------------------------------------------
            $privateKey = file_get_contents($directorio.$nombre_base.".crt");
        if (!$privKey = openssl_pkey_get_private($privateKey))
            die('Loading Private Key failed');
            $pubKey  = file_get_contents($directorio.$nombre_base.".pub");
        }

    return array($privateKey, $pubKey);
    }

    //------------------------------------------------------------
    function crear_llaves_RSA($ruta_base) {
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // PROPÓSITO:
    // generacion de llaves RSA en php
    // ENTRADAS:
    // $ruta_base: Ruta de los archivos a generar sin extensión.
    // SALIDAS:
    // Se generarán dos archivos, uno con la llave privada con
    // extensión .crt, el otro con llave pública con extensión
    // .pub; la función retorna tanto la llave pública como la
    // privada en un arreglo.
    //------------------------------------------------------------
        $config = array(
            "private_key_bits" => 1024,
            "private_key_type" => OPENSSL_KEYTYPE_RSA,
        );

        $llavePrivadaCruda = openssl_pkey_new($config);
        openssl_pkey_export_to_file($llavePrivadaCruda, $ruta_base.".crt");
        $privateKey = file_get_contents($ruta_base.".crt");
        openssl_pkey_export($llavePrivadaCruda, $privKey);

        $pubKeyData = openssl_pkey_get_details($llavePrivadaCruda);
        $pubKey = $pubKeyData["key"];
        file_put_contents($ruta_base.".pub", $pubKey);
        openssl_free_key($llavePrivadaCruda);

    return array($privateKey, $pubKey);
    }

    //------------------------------------------------------------
    function Mostrar($valor) {
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // PROPÓSITO: Genera el código HTML para presentar una
    // variable embebida en la página.
    // ENTRADAS:
    // $valor: el valor a presentar.
    // SALIDAS: código html que permite visualizar la variable.
    //------------------------------------------------------------
        $retorno = htmlentities(stripslashes(var_export($valor, true)));
        $retorno = "<pre>$retorno</pre>";
        return $retorno;
    }

?>

目录树必须如下所示:

├── script.php
└── lib
    └── jsencrypt.js

还有一个在公共区域之外可由 php 写入的目录,名为

/path/to/key/directory/apache/writable/

【讨论】:

    【解决方案2】:

    此答案由 Op. 发布为 edit to the question。虽然被问到post as an answer,但它被保留为对原始问题的编辑。


    我已经通过使用不同的 javascript 加密库解决了这个问题。方法如下:

    我使用 Paul Tero 的更简单的 DES 库作为 javascript 和标准 PHP 解密函数。

    Javascript:

    var key = '12345678';
    var encrypted = stringToHex(des(key, 'This is something private', 1));
    

    注意事项:

    • 密钥必须(显然)有偶数个字符。我使用了 8。我想 16 或更多会更好,但正如您可能知道的那样,我对密码学一无所知。
    • 函数 stringToHex 包含在 DES javascript 代码中。

    PHP

    $key = '12345678'
    $unencrypted = trim(mcrypt_decrypt(MCRYPT_DES
                                      , $key
                                      , safeHexToString($encrypted)
                                      , MCRYPT_MODE_ECB))
    

    使用这个辅助函数:

    /**
     *
     * Convert input HEX encoded string to a suitable format for decode
     * // Note: JS generates strings with a leading 0x
     *
     * @return string
     */
    function safeHexToString($input)
    {
        if(strpos($input, '0x') === 0)
        {
            $input = substr($input, 2);
        }
        return hex2bin($input);
    }
    

    这对我有用。带有前导“0x”的 javascript 十六进制编码字符串给我带来了一些问题,因为我正在以一种特别愚蠢的方式进行调试。

    所以我希望这会有所帮助。

    【讨论】:

      【解决方案3】:

      我使用了同一个库。必须确保 des() 结果被修剪,因为后面有一些奇怪的字符并且不匹配。

      $password = trim(des("12345678", hexToString("1212121121"), 0, 1, null));
      

      希望对某人有所帮助。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-03-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多