## RSA Encryption in Pure PHP

**Note:** I didn’t really make these classes to be portable, so in the RSA_Handler class, there are a few things that I did my own way (mostly in the generate_keys and encrypt functions), but you should still be able to see how it works. If you want the most basic usage, use an RSA_keymaker object and the make_keys function. The function will return an array of keys (in number form) where $keys[0] is the public key, $keys[1] is the private key and $keys[2] is the modulo. The keys generated with the default function are around 1024 bits (a 310 digit modulo).

~~In the future I’ll probably tweak these so that they’re useable on any PHP build (after 4.0 or so).~~

**EDIT:** I have updated the code! It is now 100% portable and full functioning. The only thing I have left out is the ability to encrypt the private key, but there are several pure PHP implementations of AES, DES3, Blowfish etc. which can be used to encrypt a private key for storage. This is released under the GPL Licence. Do whatever you want with it, but know there’s no warranty express or implied. My work here is done 🙂

**UPDATE:** The code is now more efficient, and uses number of bits to determine key sizes (instead of number of digits). The version number is included in the code so you can always check to see if there is a newer version available (but don’t hold your breath)

IT’S NOW ON GITHUB!!! https://github.com/stevish-com/RSA-in-PHP/. Please feel free to suggest your changes there and I’ll be happy to commit them. I’d love to spend more time on this but I’m swamped. Thanks to all the commenters!

April 17th, 2009 at 9:15 am

Wow!

I was looking for a way to encrypt data using RSA public keys from a web host. No hope for a PHP stuff but you did it! And more!..

Thank you for that!

Great job!

June 4th, 2009 at 6:42 am

Hi. Thanks for your code. I’m trying to get this to work but I’m stuck. I added some debug-echos and found that I needed to uncomment the $maxed = false; and $mined=false; in make_prime to get some echoes at all. Any help is appreciated. Thanks.

if($maxed && $thisdigit $thismin)

$mined = false;

June 12th, 2009 at 10:02 am

my host does not have the bcmath extension installed. could you use this instead?:

http://phpseclib.sourceforge.net/documentation/math.h tml#math_biginteger

it does what bcmath does but only uses php, instead. well, it uses bcmath if it is available, for speed, but if it is not, it uses a pure-php implementation.

thx!

September 17th, 2009 at 3:39 am

Oops!… found the infinite loop at:

while($thisdigit = $thismin)

$thisdigit = ($thisdigit + rand(0,9)) % 10;

September 17th, 2009 at 3:45 am

while($thisdigit <= $thismax && $thisdigit >= $thismin) {

$thisdigit = ($thisdigit + rand(0,9)) % 10;

when

$thismax === 9

$thismin === 0

September 23rd, 2009 at 9:39 am

As Tester says, if $thismin == 0 and $thismax == 9, the $thisdigit while loop would thrash for infinity. In haste, and because the primality check later modifies the number anyway (so it doesn’t really matter), I dropped some entropy but fixed the loop by changing:

while($thisdigit = $thismin)

to:

while($thisdigit = $thismin)

Likewise, a couple lines down, the primality check while loop would sometimes go on for far too long, so I essentially random-restarted (valid AI theory), by changing:

$tries++;

to:

if (++$tries > 2001) return make_prime($bits);

(2001 is completely arbitrary, but works in my test cases. Also note ++$i is 1/3 faster than $i++, which matters for big loops.)

With the above changes, the script generates keypairs in decent time, and self-rectifies if the original guess was nowhere near a prime. Since the problem space is fairly sparse, it’s better to guess again than to keep incrementing (at least, it seems to solve the problem much faster.)

September 23rd, 2009 at 9:41 am

Oops. The top of that should read (but was HTML-tag filtered out):

I changed:

while($thisdigit <= $thismax && $thisdigit >= $thismin)

to:

while($thisdigit < $thismax && $thisdigit >= $thismin)

September 23rd, 2009 at 2:59 pm

Also return make_prime($bits); should become return $this->make_prime($bits);

My bad.

September 28th, 2009 at 11:57 am

insid0r, for me, is the logic that’s wrong. This is my solution:

(…)

while( ($thisdigit != 1 && $thisdigit != 3 && $thisdigit != 7 && $thisdigit != 9) || ($thisdigit < $thismin || $thisdigit > $thismax) )

$thisdigit = ($thisdigit + rand(0,9)) % 10;

else

while($thisdigit < $thismin || $thisdigit > $thismax)

$thisdigit = ($thisdigit + rand(0,9)) % 10;

October 1st, 2009 at 6:43 pm

This statement -in make_keys() function- will cause a too much short prime number… and invalid (de)crypt:

while( substr($u, -16, 2) < (substr($v, -16, 2) + 2) && substr($u, -16, 2) > (substr($v, -16, 2) – 2) ) {

//Make sure the 2 primes are at least 1 quadrillion numbers apart

$v = $this->make_prime(intval($digits/2));

}

The right way is:

while( substr($u, -16, 2) < (substr($v, -16, 2) + 2) && substr($u, -16, 2) > (substr($v, -16, 2) – 2) ) {

//Make sure the 2 primes are at least 1 quadrillion numbers apart

$v = $this->make_prime(floor($bits/2) – $variant);

}

January 5th, 2011 at 9:54 am

Hey, just a little remark, in the one method with $chars = “….” you should be transforming this to a more unicode-friendly thing like using the dec/hex value instead of the chars themselves. $chars = “x1234x1235x1236…..” like this i mean.

btw a hoster w/o common libs like bcmath,socks,curl,..enabled isnt worth a penny, sorry bro 😛

January 5th, 2011 at 11:35 am

Does anyone have a fully revised version of this class (with the changes above) that works?

April 18th, 2011 at 10:44 am

thankyou:)

some great ideas here:)

May 20th, 2011 at 7:03 am

I’ve merged in all the changes listed here and decryption still does not seem to work correctly.

Any hints?

September 2nd, 2011 at 10:33 am

Same here… this scripts doesn’t seem to work.

PHP Notice: Undefined property: RSA_keymaker::$primes in rsa.php on line 149

PHP Fatal error: Maximum execution time of 30 seconds exceeded in rsa.php on line 232

September 23rd, 2011 at 9:19 pm

Hey,

I borrowed some code from this. Anyways, I rewrote you prime list init in the constructor. The new code is about six times faster and less memory intense. Anyways, here it is if interested:

Original:

public function initPrimes2($max_prime = 20000) {

if(is_null($this->primes)) {

for ($i = 0; $i $num) {

if(!$num) {

continue;

}

$j = $i;

for ($j += $i; $j primes[] = $num;

}

}

}

}

New code:

public function initPrimes($max_prime = 20000) {

if(is_null($this->primes)) {

//Add 2 and 3

$this->primes[] = 2;

$this->primes[] = 3;

//Make list of numbers not divisible by 2 and 3

$pc_list = array();

$stop_at = $max_prime/6;

for($i=1; $i $bool) {

if(!$pc_list[$pc]) {

continue;

}

$cur = $pc;

$stop_at = $max_prime/(6*$cur);

while($cur $bool) {

if($bool) {

$this->primes[] = $pc;

}

}

}

}

September 24th, 2011 at 5:46 am

Hey,

I got your code working. The main problem I found (other then a few minor glitches such as inf. loop and a few typos) is the chars string in the “long_base_convert” function. Basically there are 95 printable standard ascii characters and the string is 145 chars. Some of the other chars require a second char index in order to use them. So, is you do a strlen() of the chars string, you get back 201 instead of 145. These extra chars are non-unique which breaks function.

So, you can use just the 94 printable chars (can’t use space) and work in base 94. Simply change the base 145 to 94 in the function calls and change the chars string to:

$chars = “0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP QRSTUVWXYZ_-+=!@#$%^*(){[}]|:,.?/`~&’;\”\\”

Alternatively you can build a chars string user the chr() function. With this you can use the full ascii table and work in base 255. function make_char_list() {

$segments = array(

48, 57, //Numbers

97, 122, //Lowercase

65, 90, //Uppercase

91, 96, //Symbols

33, 47, //Symbols — Skip space (33)

58, 64, //Symbols

0, 31, //Beginning of table

123, 255 //End of table

);

$str = “”;

for($i=0; $i < count($segments); $i+=2) {

for($j = $segments[$i]; $j <= $segments[$i+1]; $j++) {

$str .= chr($j);

}

}

return $str;

}

Just don't print out the encrypted data and treat it as binary. Also, ASCII index 0 is the null character, so, that index may want to be skipped if it causes a problem with string (not sure how php does it). Anyways, it seems to work for me so far.

I have made quite a few modifications to the code. If you want my version, let me know where to send it.

October 26th, 2011 at 10:36 am

@Alan I would be interested in your modified version myself as I prefer to use this over

phpseclib. Even tho I have full control over the Server, I won’t need the full features

of phpseclib and would prefer a simple implementation. I can be reached at:

mythdis-forumewfps (at) everonworlds (dot) com Cheers

October 26th, 2011 at 10:37 am

woops, forgot, make that (at ) yahoo.com (its one of my disposables to avoid

spam 🙂

December 12th, 2011 at 1:33 pm

I was getting an error …

Undefined property: RSA_keymaker::$primes

…and found a simple correction:

— rsa-classes.txt.orig 2011-12-12 13:31:51.757542662 -0500

+++ rsa-classes.txt 2011-12-12 13:30:54.477542217 -0500

@@ -144,7 +144,7 @@

static $primes = null;

function __construct() {

– if(is_null($this->primes)) {

+ if(isset($this->primes)) {

//Make $this->primes an array of all primes under 20,000

//We will use this list to rule out the “easy” composite (non-prime) numbers

October 2nd, 2012 at 2:21 am

It has taken too long to call serialize(). It doesn’t seem to return. What shall I do?

May 11th, 2013 at 10:21 am

actually i want a working version of these, kindly assist..erros pease me off.

July 28th, 2013 at 5:34 am

Hallo Alan

Please send your code to my email .. fauzie@mail.com

TQ

August 27th, 2013 at 3:01 am

So Alan are you sending your code to anyone?

I would be interested too…

mani . reini (at) gmail . com

November 4th, 2013 at 5:13 am

looks great, will try to use it on my webserver this weeks.

excited to see if I can get it to work.

Many thanks in advance!

January 11th, 2014 at 10:57 am

It works great with three changes.

Great job Stevish ! Thank you ! ! The best explanation of RSA I’ve seen. It really helped to put all the pieces together for me.

I couldn’t get it to work at first. I found a typo and three other problems. The biggest of which, by far, was the prime number generator. I tried to fix it, but gave up and wrote an alternate.

By next week sometime. I will have a javasccript PHP library to make passing all data between the client and server easy to encrypt. It will have four functions, encrypt on the client side, encrypt on the server side, and decrypt client and server side. Plus an initialization to get the keys. I will offer these for download for anyone to use, because I encourage the ubiquitous use of encryption.

Look for them by before Feb 2014. In the mean time, please check over my work. Please, post if you find any mistakes I made.

Jerry

Make these four changes to the code posted here, the test lines will work

$text = “Peter Piper picked a peck of pickled peppers”;?

$RSA = new RSA_Handler();?

$keys = $RSA->generate_keypair(512);?

$encrypted = $RSA->encrypt($text, $keys[0]);? $decrypted = $RSA->decrypt($encrypted, $keys[1]);?

echo $decrypted; //Will print Peter Piper picked a peck of pickled peppers

First reduce the number of bits to 512. 1024 takes too long. PHP often times out. I have a fix for this that will come out in the download next week. Look for it here. I will also post it in some programming forums, CodeCall and Stack Overflow.

Change the test line:

$keys = $RSA->generate_keypair(1024);

to:

$keys = $RSA->generate_keypair(512);

This line is a typo:

$v = $pm->make_prime(intval($digits/2));

change the $pm to $his:

$v = $this->make_prime(intval($digits/2));

Some characters used in these two lines, were simply problem characters for some systems:

$out .= $this->long_base_convert($cryptblock, 10, 145) . ” “;

$block = $this->long_base_convert($block, 145, 10);

change the 145 to 87

$out .= $this->long_base_convert($cryptblock, 10, 87) . ” “;

$block = $this->long_base_convert($block, 87, 10);

and in the line:

$chars = “0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP QRSTUVWXYZ_-+=!@#$%^*(){[}]|:,.?/`~•¤¶§Çüéâ àåçêëèïîìÄÅÉæÆôöòûùÿÖÜ¢£¥ƒ áíóúñÑªº¿¬½¼¡«»¯ßµ±÷;”;

change to:

$chars = “0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP QRSTUVWXYZ_-+=!@#$%^*(){[}]|:,.?/`~”;

In these two lines 1024 bits is forced regardless of what value is passed to it in “$keys = $RSA->generate_keypair(1024);”

function generate_keypair($bits=1024 ) {

function make_keys($bits=1024, $u = false, $v = false) {

remove “=1024″ just cut it right out.

function generate_keypair($bits ) {

function make_keys($bits, $u = false, $v = false) {

Finally the big one. The prime number generator is broke.

Replace the whole function or class method:

function make_prime($bits) { . . . }

with this method instead:

function make_prime($bits) {

$b= array(1,3,7,9);

$n= ”;

do {

$t= microtime( true );

$n= ”;

while (strlen($n) microtime( true ) && ! $this->is_prime( $n))

$n= substr( $n, 0, -10) . ( intval( substr( $n, -10)) + 2);

}while ( ! $this->is_prime( $n));

return $n;

}

completely remove the function:

function entropyarray($digits) { . . . }

You don’t need it anymore.

Would love your critique Stevish.

January 11th, 2014 at 11:33 am

while (strlen($n) microtime( true ) && ! $this->is_prime( $n))

should read

while ($t + 1.1 > microtime( true ) && ! $this->is_prime( $n))

January 11th, 2014 at 11:37 am

Check that. I copy and pasted completely wrong lines. Think I can get it right this time?

The function make_prime($bits) should be:

function make_prime($bits) {

$b= array(1,3,7,9);

$n= ”;

do {

$t= microtime( true );

$n= ”;

while (strlen($n) microtime( true ) && ! $this->is_prime( $n))

$n= substr( $n, 0, -10) . ( intval( substr( $n, -10)) + 2);

}while ( ! $this->is_prime( $n));

return $n;

}

January 11th, 2014 at 11:43 am

OK it wasn’t my fault. the great than character messed up the post. This time the code should post correctly. Stevish, If you could please delete and paste this in the appropriate place. in my original post.

function make_prime($bits) {

$b= array(1,3,7,9);

$n= ”;

do {

$t= microtime( true );

$n= ”;

while ( floor( $bits / 3 ) > strlen($n))

$n.= mt_rand(0,9);

str_shuffle( $n );

$n.= $b[ mt_rand(0,3) ];

while ($t + 1.1 > microtime( true ) && ! $this->is_prime( $n)){

$n= substr( $n, 0, -10) . ( intval( substr( $n, -10)) + 2);

}while ( ! $this->is_prime( $n));

return $n;

}

March 11th, 2014 at 11:20 pm

Hi

There is someone who could give me a working code of this?

April 22nd, 2014 at 10:32 pm

Well I haven’t had much time, but I threw this up onto github (https://github.com/stevish-com/RSA-in-PHP/). Thanks to all the commenters, and maybe we’ll be able to get this thing fixed up over at github now.

May 16th, 2014 at 8:35 am

Well, nothing of this works!

nice work, but it should work. especially if many people commented about mistakes.

This is what i get:

Strict Standards: Accessing static property RSA_keymaker::$primes as non static in /srv/users/serverpilot/apps/www/public/rsa_tests/rsa- in-php.php on line 162

Notice: Undefined property: RSA_keymaker::$primes in /srv/users/serverpilot/apps/www/public/rsa_tests/rsa- in-php.php on line 162

Strict Standards: Accessing static property RSA_keymaker::$primes as non static in /srv/users/serverpilot/apps/www/public/rsa_tests/rsa- in-php.php on line 184

Fatal error: Maximum execution time of 30 seconds exceeded in /srv/users/serverpilot/apps/www/public/rsa_tests/rsa- in-php.php on line 245

September 20th, 2014 at 9:27 pm

This does not work!!!

January 7th, 2015 at 10:22 am

Please, this does not work. for the same reason above.. please who can help me to fix it ?

August 16th, 2015 at 9:03 am

Not working on php 4.4

December 15th, 2016 at 5:53 am

i need hai text in rsa encrypted code