RSA encryption/decryption compatible with Javascript and PHP

Posted on

RSA encryption/decryption compatible with Javascript and PHP – Here in this article, we will share some of the most common and frequently asked about PHP problem in programming with detailed answers and code samples. There’s nothing quite so frustrating as being faced with PHP errors and being unable to figure out what is preventing your website from functioning as it should like php and javascript . If you have an existing PHP-based website or application that is experiencing performance issues, let’s get thinking about RSA encryption/decryption compatible with Javascript and PHP.

I’d like to encrypt in Javascript and then decrypt in PHP. There are RSA implementations for Javascript and PHP but they are not compatible. I cannot decrypt in PHP correctly what I had encrypted with Javascript.

Does anyone know a library/code that will work both with Javascript and PHP?

Thanks.

Solution :

Try the following simple example.

It is using a open source javascript library https://github.com/ziyan/javascript-rsa

HTML/JAVASCRIPT:

<script language="JavaScript" type="text/javascript" src="jsbn.js"></script>
<script language="JavaScript" type="text/javascript" src="rsa.js"></script>

<script language="JavaScript">

    function encryptData(){

        //Don't forget to escape the lines:
        var pem="-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDfmlc2EgrdhvakQApmLCDOgP0n
NERInBheMh7J/r5aU8PUAIpGXET/8+kOGI1dSYjoux80AuHvkWp1EeHfMwC/SZ9t
6rF4sYqV5Lj9t32ELbh2VNbE/7QEVZnXRi5GdhozBZtS1gJHM2/Q+iToyh5dfTaA
U8bTnLEPMNC1h3qcUQIDAQAB
-----END PUBLIC KEY-----";

        var key = RSA.getPublicKey(pem);

        element=document.getElementById('password');
        element.value=RSA.encrypt(element.value, key);
    }
</script>

<form method='POST' id='txtAuth' onsubmit='encryptData()'>
    <input type='text' name='username'/>
    <input type='password' name='password' id='password' placeholder="password"/>
    <input name='submit' type='submit' value='Submit'>
</form>

PHP:

<?php

if (isset($_POST['password'])) {

    //Load private key:
    $private = "-----BEGIN RSA PRIVATE KEY-----
    MIICXAIBAAKBgQDfmlc2EgrdhvakQApmLCDOgP0nNERInBheMh7J/r5aU8PUAIpG
    XET/8+kOGI1dSYjoux80AuHvkWp1EeHfMwC/SZ9t6rF4sYqV5Lj9t32ELbh2VNbE
    /7QEVZnXRi5GdhozBZtS1gJHM2/Q+iToyh5dfTaAU8bTnLEPMNC1h3qcUQIDAQAB
    AoGAcbh6UFqewgnpGKIlZ89bpAsANVckv1T8I7QT6qGvyBrABut7Z8t3oEE5r1yX
    UPGcOtkoRniM1h276ex9VtoGr09sUn7duoLiEsp8aip7p7SB3X6XXWJ9K733co6C
    dpXotfO0zMnv8l3O9h4pHrrBkmWDBEKbUeuE9Zz7uy6mFAECQQDygylLjzX+2rvm
    FYd5ejSaLEeK17AiuT29LNPRHWLu6a0zl923299FCyHLasFgbeuLRCW0LMCs2SKE
    Y+cIWMSRAkEA7AnzWjby8j8efjvUwIWh/L5YJyWlSgYKlR0zdgKxxUy9+i1MGRkn
    m81NLYza4JLvb8/qjUtvw92Zcppxb7E7wQJAIuQWC+X12c30nLzaOfMIIGpgfKxd
    jhFivZX2f66frkn2fmbKIorCy7c3TIH2gn4uFmJenlaV/ghbe/q3oa7L0QJAFP19
    ipRAXpKGX6tqbAR2N0emBzUt0btfzYrfPKtYq7b7XfgRQFogT5aeOmLARCBM8qCG
    tzHyKnTWZH6ff9M/AQJBAIToUPachXPhDyOpDBcBliRNsowZcw4Yln8CnLqgS9H5
    Ya8iBJilFm2UlcXfpUOk9bhBTbgFp+Bv6BZ2Alag7pY=
    -----END RSA PRIVATE KEY-----";
    if (!$privateKey = openssl_pkey_get_private($private)) die('Loading Private Key failed');

    //Decrypt
    $decrypted_text = "";
    if (!openssl_private_decrypt(base64_decode($_POST['password']), $decrypted_text, $privateKey)) die('Failed to decrypt data');

    //Decrypted :) 
    var_dump($decrypted_text);

    //Free key
    openssl_free_key($privateKey);
}
?>

Enjoy!

Here is an JavaScript RSA encryption library: http://www.ohdave.com/rsa/

And I think you could use something like this class to decrypt the generated encrypted string – http://www.phpclasses.org/browse/package/4121.html

Let me know if you manage get this work together, as I am myself looking into this subject (I actually found this post looking for this answer myself 馃槢 ) .

Edit: Look, I’ve also found this – http://www.sematopia.com/?p=275 – seems related to the previous two as well…

If you set up your server to use SSL then you can have encrypted transmission via ajax using https. That is probably the best way to encrypt data between javascript and php. If you want to make it yourself there is a big chance you will screw up somewhere and the system wont be secure.

Google on how to set up https for your server.

I assume you have a valid reason to do that other than to do https yourself, so I’d say that if you stick to the standards you should be able to easily decrypt with whatever technology supports these standards : ie it should work

For example if you encrypt your data in PKCS#7 format, be sure that your php library knows that the input data is PKCS#7.

Also be sure that your encryption key is not scrambled between server and client.
Did you try to decrypt your data with your javascript library ?

Hope this can help…

Maybe you can help by putting the code you’re using for both js and php.

Also, maybe you could be more specific on why you need to use js and php. Maybe you could use only php, and AJAX (to query the same php function) where you were using js.

I am not one to toot my own horn but I have a project at github.com that will perform this functionality.

A private key is generated on the server, a public key and pkcs#7 certificate is also derived from the private key. The public key is sent to the client at which time each form element assocated with the specified form is encrypted prior to being submitted to the server.

It is 100% OpenSSL compatibile as it uses the PHP OpenSSL extension to generate, encrypt and decrypt data.

https://github.com/jas-/jQuery.pidCrypt/

This project is not as secure as PGP because the JavaScript will not sign and encrypt emails until the form data is sent to the server, but the form data that is to be encrypted and or signed is encrypted using RSA public key encryption prior to being sent to the server.

Again the project is not complete in terms of the authentication and email signing but for ordinary form encryption using a public key it works very well.

I find this jsencrypt library (http://travistidwell.com/jsencrypt), after 2 days trying I got my solution.

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.

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/

Leave a Reply

Your email address will not be published. Required fields are marked *