RSA: Encrypting in JavaScript and Decrypting in PHP

I came across a really cool JavaScript library a while back that allows you to do client-side RSA encryption & decryption. If you don’t know much about RSA, read up on it here. Essentially, it’s an algorithm for public/private key encryption/decryption. You encrypt using the Public Key and Modulus and decrypt using the Private Key and Modulus.

More often than not, you would want to encrypt client-side (browser) and decrypt server-side. For my purposes, I wanted to encrypt passwords 13 characters or less in JavaScript and decrypt them in PHP. The creator of the RSA library also created a convenient Windows application that generates RSA keys for you. Using that program I created the following 128bit keys:

e = Public Key = 553799486327459813656784787218239817 (6aa86c39bbe678f7f7967a587a1149)
d = Private key = 1401845535567450041611005523755666809 (10dfc5235ef69148bdfdc6832860d79)
m = Modulus = 11570601966616835094916890432003700913 (8b46ab8a951615d07b66bdd2420f8b1)

By default the RSA library outputs encrypted strings in Hex, but because there is no built in PHP function to convert from Hex to Binary, I decided to modify the RSA.js file slightly (within the encryptedString function – line 62) to output the full Integer:

var text = key.radix == 16 ? biToDecimal(crypt)/*biToHex(crypt)*/ : biToString(crypt, key.radix);

The Key Generator also conveniently creates the JavaScript needed to create the new key pair. I’ve removed the decryption (Private) key:

setMaxDigits(19);
key = new RSAKeyPair(”6aa86c39bbe678f7f7967a587a1149″, “”, “8b46ab8a951615d07b66bdd2420f8b1″);
w.value = encryptedString(key,”1234567890123″ + “\x01″);

The trick here is adding “\x01″ to the end of the string you want to encrypt. The PEAR library used to decrypt the encrypted string will look for this, and throws an error if not found. That said, the above encrypted string is:

1276850306890326374886324100793107985

The PEAR package used to decrypt this string is called Crypt_RSA. You install this just like any other PEAR package:

PEAR install Crypt_RSA

The package depends on one of 3 Math libraries (BCMath, GMP or big_int), of which BCMath is slowest but installed by default (at least in my version of PHP 5.2.0-8+etch13 & XAMPP-win). The Crypt_RSA package works in binary, so the keys and encrypted text needs to be converted before calling the package. The following code converts the Integers to Binary and performs the decryption. Bare in mind your dealing with very large numbers, so you need to use one of the above Math libraries to work with them.

require_once ‘Crypt/RSA.php’;
//
$wrapper_name = “BCMath”;
$math_obj = &Crypt_RSA_MathLoader::loadWrapper($wrapper_name);
//
$d = $math_obj->int2bin(”1401845535567450041611005523755666809″);
$m = $math_obj->int2bin(”11570601966616835094916890432003700913″);
//
$pk = new Crypt_RSA_Key($m, $d, “private”, $wrapper_name);
$rsa_obj = new Crypt_RSA;
$rsa_obj->setParams(array(’dec_key’ => $pk));
$dec = $rsa_obj->decryptBinary($math_obj->int2bin(”1276850306890326374886324100793107985″));
echo $dec;

You can download my sample code here.

Related Articles:

Follow me on Twitter!

Your Ad Here

16 Responses to “RSA: Encrypting in JavaScript and Decrypting in PHP”

  1. hiren says:

    I need to encrypt my html form data by public key which is then stored in database in encrypted form & when user wants to view it , the data will fetch from database and decrypted on browser by user’s private key.

    How can I achieve it? can yoy please help mw out?

    With Regards
    Hiren

  2. Eun says:

    Is it posible without the pecl package? Maybe with a class like http://www.phpclasses.org/browse/package/4121.html ?

  3. dennis says:

    I have try your sample code. it work fine. but original data seems can’t exceed 13 bytes.
    I want send about 100 bytes to server. How can I change the limitation.

    Best regards

    Dennis

  4. George A. Papayiannis says:

    Dennis, I hear you – I’m not sure – if you find out, let me know…

  5. Steven says:

    To encrypt longer strings you have to insert the “\x01″ in at the end of every chunk.
    Try this in the Javescript:

    setMaxDigits(19);
    // Put this statement in your code to create a new RSA key with these parameters
    key = new RSAKeyPair(
    "6aa86c39bbe678f7f7967a587a1149",
    "",
    "8b46ab8a951615d07b66bdd2420f8b1"
    );
    s = trim("supersuperlongstringtoencrypt");
    sl = s.length;
    var i = 0;
    var a = "";
    while (i < sl)
    {
    if ((i+1) % key.chunkSize == 0)
    a += "\x01";
    a += s.charAt(i);
    i++;
    }
    if ((i+1) % key.chunkSize != 0)
    a += "\x01";

    w.value = encryptedString(key, a);

  6. panda says:

    thanks for your share! i really need such informations!

  7. Zashkaser says:

    I’m glad that after surfing the web for uch a long time I have found out this information. I’m really lucky.

  8. Roberto says:

    Steven, I’m not able to have your advice for long string running correctly! Anyone encountered the same problem and has a solution?

  9. Emmanuel says:

    Thanks, I’ve been looking for this..

  10. George A. Papayiannis says:

    Hi Roberto,

    I wasn’t able to get Steven’s code to work exactly either – I changed it around a bit and managed to now be able to encrypt string less than 24 – 26(?) characters (check that). Here is the function directly from a JS class that I made – if anyone knows how to make this work for any size string please post the code.


    encrypt: function(s) {
    setMaxDigits(19);
    var key = new RSAKeyPair("", "", "");
    var sl = s.length;
    var i = 0;
    var j = 1;
    var a = "";
    while (i < sl) {
    if ((i+1) % key.chunkSize == 0) {
    a += "\x01";
    j++;
    }
    a += s.charAt(i);
    i++;
    }
    if ((i+1) % key.chunkSize != 0 || (i+1) == (key.chunkSize * j)) {
    a += "\x01";
    }
    return encryptedString(key,a);
    },

  11. Hristo says:

    Hello,

    i’m trying to do following:
    1.) Create private and public key pairs with PEAR Crypt_RSA
    2.) Sign a dummy message using the public key from 1.) but in C# application (i’m using base64_encode($public_key->getModulus()) and base64_encode($public_key->getExponent()) in php and after that manually creating an .NET xml private key using the two strings for Modulus and Exponent xml nodes)
    3.) Decrypt the message encrypted from C# app. but again in PHP and PEAR Crypt_RSA from 2.)

    I’m stuck at point 3.)…… Crypt_RSA aways says that the tail is invalid

    the code which i use in my C# app. is:

    // I did adding of “\x01″ in the end of the string as you are advising above
    string inputString = “hello\x01″;
    string xmlString = “r+7rlF0e/v2n9Pj1muEkyxnrLqMsWf9AWWSwx3RY9QfuD71u1xfLmuu46HuATZtxxq3QVqGwKF6Fm5Z4ATUSjg==AQAB”;
    RSACryptoServiceProvider rsaCryptoServiceProvider = new RSACryptoServiceProvider();
    rsaCryptoServiceProvider.FromXmlString(xmlString);
    // The length of certificate is 1024 so i do not need here to try split the input string to chunks
    byte[] resultByteArray = rsaCryptoServiceProvider.Encrypt(Encoding.ASCII.GetBytes(inputString), false);
    return Convert.ToBase64String(resultByteArray);

    Please, if anyone can give a hand here – do it i’m almost tired to do tests with no luck :-)

    Thanks in advanced & cheers!

  12. shrui says:

    Thanks for your share, George. Now I generate key-pair with Crypt_RSA. The code like this:
    function genKey($keylen){
    require_once(’Crypt/RSA.php’);
    $wrapper = “GMP”;//or other
    $math_obj = &Crypt_RSA_MathLoader::loadWrapper($wrapper);
    $key_pair = new Crypt_RSA_KeyPair($keylen);
    if (!$key_pair->isError()){
    $public_key = $key_pair->getPublicKey();
    $private_key = $key_pair->getPrivateKey();
    $e = gmp_strval($math_obj->bin2int($public_key->getExponent()));
    $d = gmp_strval($math_obj->bin2int($private_key->getExponent()));
    $m = gmp_strval($math_obj->bin2int($public_key->getModulus()));
    mysql_query(”update conftab set e=’”.$e.”‘,d=’”.$d.”‘,m=’”.$m.”‘”);
    }
    }
    Another function converts e and m to hex string for JavaScript’s encrypt
    function. The MaxDigits depeneds on $keylen in PHP code.
    function gmp_dechex($num){
    $result = ”;
    do{
    $result = sprintf(’%02x’,intval(gmp_mod($num,256))).$result;
    $num = gmp_div($num, 256);
    }while(gmp_cmp($num, 0));
    return ltrim($result,’0′);
    }

    Best regards

    shrui

  13. shrui says:

    to coutomize my own encrypt exponent, with a little change to the original code:

    function generate($key_len = null, $pubexp = null)//apply the second parameter
    //...
    //original code in KeyPair.php(CRYPT_RSA-1.0.0): line 245~271{
    if($pubexp != null&&$this->_math_obj->cmpAbs($pubexp,2)>0)
    $e = $this->_math_obj->nextPrime($this->_math_obj->dec($pubexp));
    else{
    while(true){
    $e = $this->_math_obj->getRand($q_len, $this->_random_generator);
    if ($this->_math_obj->cmpAbs($e,2)_math_obj->nextPrime($this->_math_obj->dec($e));
    break;
    }
    }
    do{
    $p = $this->_math_obj->getRand($p_len, $this->_random_generator, true);
    $p = $this->_math_obj->nextPrime($p);
    do{
    do{
    $q = $this->_math_obj->getRand($q_len, $this->_random_generator, true);
    $tmp_len = $this->_math_obj->bitLen($this->_math_obj->mul($p, $q));
    if ($tmp_len $key_len) $q_len--;
    } while ($tmp_len != $key_len);
    $q = $this->_math_obj->nextPrime($q);
    $tmp = $this->_math_obj->mul($p, $q);
    } while ($this->_math_obj->bitLen($tmp) != $key_len);
    // $n - is shared modulus
    $n = $this->_math_obj->mul($p, $q);
    // generate pubexp ($e) and private ($d) keys
    $pq = $this->_math_obj->mul($this->_math_obj->dec($p), $this->_math_obj->dec($q));
    }while($this->_math_obj->cmpAbs($this->_math_obj->mod($pq,$e),0)==0);//mod() is a new method
    //original code in KeyPair.php(CRYPT_RSA-1.0.0): line 245~271}

  14. shrui says:

    [code] destroy code....
    function generate($key_len = null, $pubexp = null)//add the second parameter $
    //...
    //original code in KeyPair.php(CRYPT_RSA-1.0.0): line 245~271{
    if($pubexp != null&&$this->_math_obj->cmpAbs($pubexp,2)>0)
    $e = $this->_math_obj->nextPrime($this->_math_obj->dec($pubexp));
    else{
    while(true){
    $e = $this->_math_obj->getRand($q_len, $this->_random_generator);
    if ($this->_math_obj->cmpAbs($e,2)_math_obj->nextPrime($this->_math_obj->dec($e));
    break;
    }
    }
    do{
    $p = $this->_math_obj->getRand($p_len, $this->_random_generator, true);
    $p = $this->_math_obj->nextPrime($p);
    do{
    do{
    $q = $this->_math_obj->getRand($q_len, $this->_random_generator, true);
    $tmp_len = $this->_math_obj->bitLen($this->_math_obj->mul($p, $q));
    if ($tmp_len $key_len) $q_len--;
    } while ($tmp_len != $key_len);
    $q = $this->_math_obj->nextPrime($q);
    $tmp = $this->_math_obj->mul($p, $q);
    } while ($this->_math_obj->bitLen($tmp) != $key_len);
    // $n - is shared modulus
    $n = $this->_math_obj->mul($p, $q);
    // generate pubexp ($e) and private ($d) keys
    $pq = $this->_math_obj->mul($this->_math_obj->dec($p), $this->_math_obj->dec($q));
    }while($this->_math_obj->cmpAbs($this->_math_obj->mod($pq,$e),0)==0);//mod() is a new method
    //original code in KeyPair.php(CRYPT_RSA-1.0.0): line 245~271}

  15. elsehair says:

    Using what essentially amounts to null padding on plaintexts means that the techniques described here are about as secure as ECB mode for block ciphers. ie. it’s not all that secure. Personally, I think OAEP padding ought to be used. PEAR’s long since abandoned Crypt_RSA doesn’t support it but phpseclib’s Crypt_RSA does:

    http://phpseclib.sourceforge.net/

  16. fie says:

    php, hex to bin
    $bin_str = pack(”H*” , $hex_str);

Leave a Reply

Line and paragraph breaks automatic.
XHTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>