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





