The values of P and Q do not match value of the Modulus of the .Net RSAParameters. According to RSA algorithm and MSDN documentation it should be: P * Q = Modulus
I generated a 512bit RSA keypair and exported it to XML by invoking:
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(512);
rsa.ToXmlString(true);
This gave me the following XML:
<RSAKeyValue>
<Modulus>rcLI1XTfmXtX05zq67d1wujnUvevBu8dZ5Q5uBUi2mKndH1FZLYCKrjFaDTB/mXW1l5C74YycVLS6msY2NNJYw==</Modulus>
<Exponent>AQAB</Exponent>
<P>1dwGkK5POlcGCjQ96Se5NSPu/hCm8F5EYwyqRpLVzgk=</P>
<Q>0AAEMHBj7CP2XHfCG/RzGldw1GdsW13rTo3uEE9Dtws=</Q>
<DP>PO4jMLV4/TYuElowCW235twGC3zTE0jIUzAYk2LiZ4E=</DP>
<DQ>ELJ/o5fSHanBZCjk9zOHbezpDNQEmc0PT64LF1oVmIM=</DQ>
<InverseQ>NyCDwTra3LiUin05ZCGkdKLwReFC9L8Zf01ZfYabSfQ=</InverseQ>
<D>EWwFTPmx7aajULFcEJRNd2R4xSXWY8CX1ynSe7WK0BCH42wf/REOS9l8Oiyjf587BhGa3y8jGKhUD7fXANDxcQ==</D>
</RSAKeyValue>
Now I successfully wrote a litte test programm to encrypt, decrypt, sign and verify data.
At the end I added a little test code:
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(512);
rsa.FromXmlString(key); // key = string with XML above
RSAParameters param = rsa.ExportParameters(true);
BigInteger p = new BigInteger(param.P);
BigInteger q = new BigInteger(param.Q);
BigInteger n = new BigInteger(param.Modulus);
BigInteger myN = BigInteger.Multiply(p, q);
Console.WriteLine("n = " + n.ToString());
Console.WriteLine("myN = " + myN.ToString());
Which gave we the following output:
n = 5200154866521200075264779234483365112866265806746380532891861717388028374942014660490111623133775661411009378522295439774347383363048751390839618325234349
myN = 23508802329434377088477386089844302414021121047189424894399694701810500376591071843028984420422297770783276119852460021668188142735325512873796040092944
Why does multiplying P and Q not equal the Modulus?
I already checked a lot of things like endian, encoding, BigInteger class, successfully encrypted, decrypted, signed, verified with the above XML keys but cannot find any explanation why P and Q multiplied is not equaling the Modulus...
Can anybody help me explain why P*Q is not the Modulus ?
All values in readable format:
Modulus = 5200154866521200075264779234483365112866265806746380532891861717388028374942014660490111623133775661411009378522295439774347383363048751390839618325234349
Exponent = 65537
P = 4436260148159638728185416185189716006279182659244174640493183003717504785621
Q = 5299238895894527538601438806093945941831432623367272568173893997325464109264
DP = -57260184070162652127728137041376572684067529466727954512100856352006444159428
DQ = -56270397953566513533764063103154348713259844205844432469862161942601135050224
InverseQ = -5297700950752995201824767053303055736360972826004414985336436365496709603273
D = 5967761894604968266284398550464653556930604493620355473531132912985865955601309375321441883258487907574824598936524238049397825498463180877735939967118353
UPDATE:
According to the answer I wrote a little extension method for the .Net BigInteger class to work correctly with the RSAParameters:
public static class BigIntegerExtension
{
public static BigInteger FromBase64(this BigInteger i, string base64)
{
byte[] p = Convert.FromBase64String(base64).Reverse().ToArray();
if (p[p.Length - 1] > 127)
{
Array.Resize(ref p, p.Length + 1);
p[p.Length - 1] = 0;
}
return new BigInteger(p);
}
public static BigInteger FromBigEndian(this BigInteger i, byte[] p)
{
p = p.Reverse().ToArray();
if (p[p.Length - 1] > 127)
{
Array.Resize(ref p, p.Length + 1);
p[p.Length - 1] = 0;
}
return new BigInteger(p);
}
}
Usage example:
BigInteger modulus1 = new BigInteger().FromBase64("rcLI1XTfmXtX05zq67d1wujnUvevBu8dZ5Q5uBUi2mKndH1FZLYCKrjFaDTB/mXW1l5C74YycVLS6msY2NNJYw==");
BigInteger modulus2 = new BigInteger().FromBigEndian(param.Modulus);
Hope this helps others with the same problem :-)
Here is my parsing of the XML parameters you provided:
As you can verify, N does indeed equal P * Q.
You cannot use the BigInteger(byte []) constructor the way you are because it expects the byte array to be in little-endian, and because Microsoft has done things bass ackwards. Instead, reverse the order of the bytes. And finally, because the bytes arrays are supposed to be twos complement and your numbers are guaranteed to be positive you must add a zero byte to the high order byte of the array if the high-order byte would otherwise be greater than or equal to 128.
BigInteger
parses arrays as signed little-endian values.RSAParameters
uses unsigned big-endian.So
i = new BigInteger(bytes.Reverse().Concat(new byte[]{0}).ToArray()))
should work.