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.


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);