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.
Have a second? Check out this great Canadian Health & Living Store based in Toronto


December 30th, 2008 at 3:23 am
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
February 13th, 2009 at 6:41 am
Is it posible without the pecl package? Maybe with a class like http://www.phpclasses.org/browse/package/4121.html ?
April 3rd, 2009 at 5:13 am
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
April 6th, 2009 at 1:42 pm
Dennis, I hear you – I’m not sure – if you find out, let me know…
May 4th, 2009 at 12:15 am
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);
June 23rd, 2009 at 2:33 am
thanks for your share! i really need such informations!
August 5th, 2009 at 12:24 pm
I’m glad that after surfing the web for uch a long time I have found out this information. I’m really lucky.
August 7th, 2009 at 6:04 am
Steven, I’m not able to have your advice for long string running correctly! Anyone encountered the same problem and has a solution?
August 11th, 2009 at 12:50 am
Thanks, I’ve been looking for this..
August 12th, 2009 at 11:37 am
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);
},
August 13th, 2009 at 11:29 am
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!
November 29th, 2009 at 7:24 pm
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
December 1st, 2009 at 4:32 pm
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}
December 1st, 2009 at 4:39 pm
[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}
December 6th, 2009 at 10:48 pm
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/
May 1st, 2010 at 6:06 am
php, hex to bin
$bin_str = pack(“H*” , $hex_str);
October 9th, 2010 at 5:53 am
Is it possible to link your javascript to working with this library?
http://phpseclib.sourceforge.net/
Is there a way to specify the key size when generating the public & private keys? 128bit is very insecure for an assymetric algorithm.
http://en.wikipedia.org/wiki/Key_size#Asymmetric_algorithm_key_lengths
November 25th, 2010 at 11:50 am
It seems to work well, thanks for this good script. I am going to use it when submitting passwords to authenticate users without SSL. I am truying to change keys each time, but you have done the hardest work.
January 7th, 2011 at 2:43 pm
none of the examples above seem to work for strings over 13 chars – and I can’t for the life of me work out why.
Any one have any ideas/explanations?
February 7th, 2011 at 7:02 am
Please am having trouble decrypting the string in php.
I think my problem is with the
$m = $math_obj->int2bin(“11570601966616835094916890432003700913″);
i think $m is supposed to be a string of binary, however, i get weird characters like y �2h�ߋi�5R�
please any idea?
April 7th, 2011 at 12:47 am
hey,
i was trying to encrypt in php & decrypt in javascript using the same method. but decryption in javascript is not giving back the proper output ,it is giving some special characters. can anybody pls tell me how to get bak de string in original format ?
April 7th, 2011 at 12:48 am
encrypting in javascript & decrypting in php is working for me.
July 18th, 2011 at 5:56 pm
Got some issues with the PHP libraries, since they are based on PHP4 and im currently using PHP5.2 … here’s the issue:
Strict Standards: Non-static method Crypt_RSA_MathLoader::loadWrapper() should not be called statically, assuming $this from incompatible context in C:\xampp\php\PEAR\Crypt\RSA\Key.php on line 159
Strict Standards: Only variables should be assigned by reference in C:\xampp\php\PEAR\Crypt\RSA\Key.php on line 159
Strict Standards: Non-static method PEAR::isError() should not be called statically, assuming $this from incompatible context in C:\xampp\php\PEAR\Crypt\RSA\ErrorHandler.php on line 150
Strict Standards: Non-static method Crypt_RSA_MathLoader::loadWrapper() should not be called statically, assuming $this from incompatible context in C:\xampp\php\PEAR\Crypt\RSA.php on line 191
Strict Standards: Non-static method Crypt_RSA_MathLoader::loadWrapper() should not be called statically, assuming $this from incompatible context in C:\xampp\php\PEAR\Crypt\RSA\MathLoader.php on line 98
Problem appears to be that PHP5 doesn’t like all those static calls to functions… Is there any easy way to handle this problem or changing the code in the libraries are the only option?