Using PHP to Encrypt, Decrypt, Sign & Verify Data with a .NET generated XML RSA Key

I’ve spent a fair amount of time researching how to sign data in PHP with a .NET generated RSA Private key that has been exported in XML format. There are many ‘half baked’ solutions out there that can only provide half of the functionality or that use exec or shell_exec to call on other components to get the required outcome. I have been looking for a ‘pure’ PHP solution to eliminate the need for extra server requirements such as OpenSSL, etc.

Eventually I found the source forge project Pure-PHP which includes an RSA library written by Jim Wiggintons which is fully PKCS#1 (v2.1) compliant. Unfortunately (for me), the library doesn’t natively support loading the .NET RSA XML exported keys, but does provide comprehensive encryption, decryption, signing & and signature verification methods.

After even more digging about, I managed to find a random post in phpseclibs own support forum by (who I think) is Jim himself explaining exactly how to sign data in PHP with .NET XML RSA. Conveniently, I found the post after I had started familiarising myself with public & private key encryption methods!

The Solution

I’ve extended Jims original RSA provider class simply adding another mechanism to load the RSA Key from the .NET XML. When I get time I may see whether we can get this integrated into the phpseclib as it would most probably prove useful to many, especially now that PHP is being more effectively supported by the IIS crews at Microsoft & the communities.

You can download the required phpseclib libraries from here.

You will then need to download the script below (RSA_XML.php) and copy it into the ‘Crypt’ directory right next to the original ‘RSA.php’ file.

/**
* Basic extension of Jim Wiggintons Pure-PHP PKCS#1 (v2.1) compliant implementation of RSA
* to allow easy use with .NET based XML keys.
*
* Requires the http://phpseclib.sourceforge.net library, this file should be placed within
* the 'Crypt' directory.
*
* This could be done better, even integrated into the lib itself, O for more time...
*
* @category   Crypt
* @package    Crypt_RSA
* @author     Oliver Green <green2go@gmail.com>
* @version    1.0 (12/04/2011)
* @link       http://www.codeblog.co.uk
* @license    http://www.gnu.org/licenses/lgpl.txt
*
* Please note that the code is copied directly from Jims 'guess' on the forums
* I have simply wrapped it to make it easier to repeatedly use.
*
* Here's an example of how to encrypt and decrypt text with this library:
* <code>
* <?php
*    include('Crypt/RSA_XML.php');
*
*    $rsa = new Crypt_RSA_XML();
*
*    $plaintext = 'terrafrost';
*
*    $rsa->loadKeyFromXML($XMLprivatekey);
*    $ciphertext = $rsa->encrypt($plaintext);
*
*    $rsa->loadKey($XMLpublickey);
*    echo $rsa->decrypt($ciphertext);
* ?>
* </code>
*
* Here's an example of how to create signatures and verify signatures with this library:
* <code>
* <?php
*    include('Crypt/RSA.php');
*
*    $rsa = new Crypt_RSA_XML();
*
*    $plaintext = 'terrafrost';
*
*    $rsa->loadKeyFromXML($XMLprivatekey);
*    $signature = $rsa->sign($plaintext);
*
*    $rsa->loadKeyFromXML($XMLpublickey);
*    echo $rsa->verify($plaintext, $signature) ? 'verified' : 'unverified';
* ?>
* </code>
*
* LICENSE: This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA  02111-1307  USA
*
* Original code  TerraFrost
* @link http://www.frostjedi.com/phpbb/viewtopic.php?f=46&t=20255
*
*/

require_once('RSA.php');

class Crypt_RSA_XML extends Crypt_RSA {

public function loadKeyfromXML($key)
{
$xml = new DOMDocument();
$xml->loadXML($key);

$modulus = new Math_BigInteger(base64_decode($xml->getElementsByTagName('Modulus')->item(0)->nodeValue), 256);
$exponent = new Math_BigInteger(base64_decode($xml->getElementsByTagName('Exponent')->item(0)->nodeValue), 256);

$this->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
$this->modulus = $modulus;
$this->exponent = $exponent;
$this->publicExponent = $exponent;
$this->k = strlen($this->modulus->toBytes());

if(isset($xml->getElementsByTagName('D')->item(0)->nodeValue)
&& isset($xml->getElementsByTagName('InverseQ')->item(0)->nodeValue)) {
$d = new Math_BigInteger(base64_decode($xml->getElementsByTagName('D')->item(0)->nodeValue), 256);
$dp = new Math_BigInteger(base64_decode($xml->getElementsByTagName('DP')->item(0)->nodeValue), 256);
$dq = new Math_BigInteger(base64_decode($xml->getElementsByTagName('DQ')->item(0)->nodeValue), 256);
$inverseq = new Math_BigInteger(base64_decode($xml->getElementsByTagName('InverseQ')->item(0)->nodeValue), 256);
$p = new Math_BigInteger(base64_decode($xml->getElementsByTagName('P')->item(0)->nodeValue), 256);
$q = new Math_BigInteger(base64_decode($xml->getElementsByTagName('Q')->item(0)->nodeValue), 256);

$this->exponents = array(1=> $dp, $dq);
$this->coefficients = array(2 => $inverseq);
$this->primes = array(1 => $p, $q);
}
}
}


4 thoughts on “Using PHP to Encrypt, Decrypt, Sign & Verify Data with a .NET generated XML RSA Key”

  1. Matthias says:

    HI, that seems like it is exactly what I am needing, BUT when I try it I always get an decryption error.
    Im using:
    $key='<XML><root><RSAKeyValue><Modulus>ybu0wuZeAMfj7Bgx6JF5H3YmnPVJT6qT/ixzGz+J55zgj2hiQbJjzokLX8up+5PQzxj+CMNZe98u1WFHrL7N7F/Y/5xxm7yiM4dPd4ggjj9wSZzS4GS0IWnseRFU6UENGyROi5T7HtfmqsFLRB2oCmxgqPRrVY7kKxqXgtb1vqU=</Modulus><Exponent>AQAB</Exponent><P>7CpBPG4afVwcnZ1gwwprnXXmjunWfbD8Pl0mQTiz/TLEt1yKvUara7mFg3Tr2+LxGYu5MWxGEX7WD1k6ARvmAw==</P><Q>2q0jZW/Yfg/cfOZJTXW6tZS8wCCqL8lqOLHdRRrE8ecNJDFgPQU4TdpO8UgnfFZXAG1Sl942zrLDJjsu2kUcNw==</Q><DP>qrtljB2BMw2rdlr3QCnBYQWCV+E/eoC/6woGhPJMVQkl4/WYK3vyo59soXueo8muSayUhuFiynUqpc/4uiKEJw==</DP><DQ>SnmNBI8C/GwKe78yV98wp4bwgb6eN4gxVP2+jjp/uKMoJptqW13bvygKmsIoMIB3QP36kx5rLBpVvD+LxWqkIQ==</DQ><InverseQ>A++v5bJBEudfMPokuzraRnhZb5dFLivu0z7uUBrPqEtCct10POn1wVM+QcfG7ROWaW2nXqZKR8zhQLxzr8CVfw==</InverseQ><D>EbSf0NtXNMpWfspA1yCteM1t5ijBHnTiOFS+ZY3WFLX4H54RwaJekCoHDYlaifIXSZXv9eTvXafg/GllWQwGFqkHMwr7GryafdY+rPHq6jQdV9NL2trB/l6CgWaJ4cnPiIcSIQZyisXGj3h4ol8xsWx6G5lFcL8kfRmUhBtImqE=</D></RSAKeyValue></root></XML>’;
    $publickey='<XML><root><RSAKeyValue><Modulus>ybu0wuZeAMfj7Bgx6JF5H3YmnPVJT6qT/ixzGz+J55zgj2hiQbJjzokLX8up+5PQzxj+CMNZe98u1WFHrL7N7F/Y/5xxm7yiM4dPd4ggjj9wSZzS4GS0IWnseRFU6UENGyROi5T7HtfmqsFLRB2oCmxgqPRrVY7kKxqXgtb1vqU=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue></root></XML>’;
    $plaintext = ‘This is a test!’;    
    $rsa->loadKeyfromXML($key);    
    $ciphertext = $rsa->encrypt($plaintext); 
    echo “Encoded:nr”.$ciphertext.”nr”;       
    $rsa->loadKeyfromXML($publickey);  
    echo “Decrypted:nr”.$rsa->decrypt($ciphertext);
     
    It shows the encrypted text but gets an decryption error when decrypting?
     
    What am I doing wrong?

  2. PBS says:

    I am also facing issue while decryption. I getencrypted privatekey from .net RSA and I need to decrypt it.
     $rsa = new Crypt_RSA_XML();   
    $rsa->loadKeyfromXML(self::$privatekey);      
     echo $rsa->verify($plaintext, $signature) ? ‘verified’ : ‘unverified’;      
    echo ‘<br> decrypted : ‘ .$rsa->decrypt($sEncrypted);
     
     
    Error:Decryption error in C:wampwwwPhpProjectCryptRSA.php on line 1583

  3. fredtma says:

    Hi,
    This is exactly what I need, however I am not in charge of the server (.net) that server me the pubKey.
    I therefore do not have the the private key to load, is there away around this?

  4. fredtma says:

    Okay I simple just add the $pubKey and it works.
    $return1 = $rsa->loadKeyfromXML($pubKey);
    //$return1 = $rsa->loadKey($pubKey);

    I have spent over 80 hours trying to get this to work, First I am going to say where have you been all my life.
    Then I will say Thank You 🙂

Leave a Reply to PBS Cancel reply

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