Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript DES encryption/PHP decryption

I have a PHP endpoint that is used by several websites. While POSTing data to the endpoint the sites use encrypted headers to identify various information. The sites use the standard PHP mcrypt_ecb function, and the endpoint decrypts the variables like this:

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

I don't really have much chance of altering the PHP endpoint, although I could conceivably make another endpoint if mcrypt_ecb doesn't play nicely.

I now have a PhoneGap app, which also needs to send the encrypted headers. Phonegap can only really use javascript to POST to a remote server. I've looked at the Google CryptoJS to try to acheive this, along side a base64 library. Note that the key is the same as the $key var above.

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

The problem is that the encrypted string (and I have tried without the base64 encode/decode) when passed through mcrypt_ecb is garbage. Apparently there are different methods of triple DES encryption?

How would I create a mcrypt_ecb compatible encrypted string in JavaScript?

like image 729
scudsucker Avatar asked Dec 06 '25 10:12

scudsucker


1 Answers

As per: http://php.net/manual/en/function.mcrypt-decrypt.php

Warning This function (mcrypt-decrypt) has been DEPRECATED as of PHP 7.1.0. Relying on this function is highly discouraged.

Then I had to investigate a lot until I discover a way to encrypt using openssl via javascript and decrypt using php.

The only problem I got is when I send a long text. That's because RSA, by definition, supports strings of limited lenght.

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

RSA, as defined by PKCS#1, encrypts "messages" of limited size. With the commonly used "v1.5 padding" and a 2048-bit RSA key, the maximum size of data which can be encrypted with RSA is 245 bytes. No more.

i.e. If I use private_key_bits of 1024 I can send

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

nothing longer. If I use private_key_bits of 512 I can send

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

nothing longer.

On long strings JavaScript console reports: "Message too long for RSA"

Then if you want to encrypt long strings you must compress and split them before javascript encryption and after decryption join and uncompress on php, I think zlib is a good solution for split/join because it is supported on javascript and php.

Maybe is better to use an AES library instead of this one like https://github.com/brainfoolong/cryptojs-aes-php

My working code is as follows:

<?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;
    }

?>

Directory tree must looks like:

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

and a directory writable by php outside of public zone named

/path/to/key/directory/apache/writable/
like image 141
gramo Avatar answered Dec 08 '25 01:12

gramo



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!