9

In PHP I am RSA encrypting a message to be decrypted by .NET application... but I keep getting a "Bad Key" exception from .NET....

For RSA encryption, I am using PEAR class Crypt_RSA-> encrypting with the public key (which is a modulus, exponent pair) I get from working encryption system in .NET...

I guess the easiest question would be-> does "Bad Key" mean it is not able to decrypt the message whatsoever? IE, it is not encrypted correctly?

The harder question is-> Is there anything specific about RSA encryption that causes quirks between .NET and PHP?

neubert
  • 15,947
  • 24
  • 120
  • 212
user312904
  • 91
  • 1
  • 1
  • 3
  • 1
    Could there be a problem with the key itself? Odd characters, problem between UNICODE and UTF-8? – Dave Swersky Apr 09 '10 at 15:11
  • You might be using different paddings. – SLaks Apr 09 '10 at 15:11
  • RE: Padding: Is the regarding the string itself? In this case, it is exactly 32 bytes - which is divisible by 8... so do I have that covered? – user312904 Apr 09 '10 at 15:17
  • Problem with key? Good question... I assume the Key is fine. The same key is used to decode messages on the backend of the .NET application (I am simply trying to replicate the backend version in PHP). Backend system works fine. And just to make sure I am not absolutely clueless there-> a public RSA key is just a modulus and exponent that is used by RSA class to encrypt the message, right? I have seen some references to certificates, which I am not using as I assumed all I needed was the public key. – user312904 Apr 09 '10 at 15:20

2 Answers2

14

Security Warning: Use OAEP, not PKCS#1.

If you want to use a solution that doesn't require the openssl extension, try phpseclib's Crypt_RSA. Examples follow:

Decryption with PKCS#1 padding:

openssl rsautl -inkey privatekey.txt -encrypt -in plaintext.txt -out ciphertext.txt

<?php
include('Crypt/RSA.php');

$rsa = new Crypt_RSA();
$rsa->loadKey(file_get_contents('privatekey.txt'));
$rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);
echo $rsa->decrypt(file_get_contents('ciphertext.txt'));
?>

Encryption with PKCS#1 padding:

<?php
include('Crypt/RSA.php');

$rsa = new Crypt_RSA();
$rsa->loadKey(file_get_contents('privatekey.txt'));
$rsa->loadKey($rsa->getPublicKey());
$rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);
echo $rsa->encrypt('1234567890');
?>

openssl rsautl -inkey privatekey.txt -decrypt -in ciphertext.txt -out plaintext.txt

Decryption with OAEP padding:

openssl rsautl -inkey privatekey.txt -encrypt -oaep -in plaintext.txt -out ciphertext.txt

<?php
include('Crypt/RSA.php');

$rsa = new Crypt_RSA();
$rsa->loadKey(file_get_contents('privatekey.txt'));
echo $rsa->decrypt(file_get_contents('ciphertext.txt'));
?>

Encryption with OAEP padding:

<?php
include('Crypt/RSA.php');

$rsa = new Crypt_RSA();
$rsa->loadKey(file_get_contents('privatekey.txt'));
$rsa->loadKey($rsa->getPublicKey());
echo $rsa->encrypt('1234567890');
?>

openssl rsautl -inkey privatekey.txt -decrypt -oaep -in ciphertext.txt -out plaintext.txt

phpseclib can be downloaded from http://phpseclib.sourceforge.net/

Good luck!

Community
  • 1
  • 1
user260294
  • 161
  • 1
  • 2
3

The Crypt_RSA on PEAR is not using PKCS#1 encoding. I suspect that is why .NET is giving you an error message.

As an example of it breaking, I created a php script using Crypt_RSA to encrypt the string "1234567" (I'll skip showing key loading):

print $rsa_obj->encryptBinary("1234567", $key_pair->getPublicKey());

Taking the output of that and piping it through the openssl command line tool gives the following error:

$ ./crypt | openssl rsautl -inkey privkey.pem -decrypt
RSA operation error
18437:error:04065084:rsa routines:RSA_EAY_PRIVATE_DECRYPT:data too large for modulus:fips_rsa_eay.c:558:

openssl expects PKCS#1 padding by default, but adding the -raw (no padding) flag to openssl doesn't help either.

Using the openssl extension in php gives the proper padding (defaults to PKCS#1, others available):

$pem = file_get_contents("pubkey.pem");
$key = openssl_pkey_get_public($pem);

$encrypted = "";
if(openssl_public_encrypt("1234567", $encrypted, $key)) {
  print $encrypted;
} else {
  print "failed\n";
}

And the decrypt code in php:

$pem = file_get_contents("privkey.pem");
$key = openssl_pkey_get_private($pem);

$enc_data = file_get_contents("openssl.crypted");
$decrypted = "";
if(openssl_private_decrypt($enc_data, $decrypted, $key)) {
  print "$decrypted\n";
} else {
  print "failed\n";
}

Certificates in the context of RSA are X.509 certificates, which are the RSA keys plus data about those keys. X.509 certificates are used in SSL, but are not required to use RSA.

ddrown
  • 170
  • 4