Certificates received from Windows CertStore: wrong public key (and incorrect modulus length)?

J

Johannes Resch

Hello,

I'm currently writing an application in C# to support message
integrity checks with signatures.
For this, I need to use X.509-Certificates from files (CER encoded) as
well as from the windows local machine certificate store.

I modified the CryptoAPI P/Invoke-Examples from MSDN
(http://msdn.microsoft.com/library/d.../encryptdecrypt2a.asp?frame=true&hidetoc=true)
to access the windows local machine certificate store from C#.

This works, I can access both public and private keys of a chosen
certificate.

For getting RSA-PK-Parameters from a C# X509Certificate, I used the
way described in http://groups.google.at/groups?hl=d...-8&[email protected].
(getting the Modulus and Exponent from raw data returned from
GetPublicKey(); see code below).


However, if I take a closer look at the public key I get from the
Windows Certificate store, there are two problems:

1.) the modulus length is always 128 Byte, no matter if I use PKs with
keylengths greater than 1024 Bit.

2.) the modulus-parameter of two public keys (one extracted from
CER-File via X509Certificate and GetPublicKey(), the other one
received from the same certificate in Windows CertStore) don't match
at all (they one from windows certstore is not just truncated, it
seems to be completely different).

If I open MMC and take a look at the certificates public key in the
windows certificate-management applet, I can see the correct public
key (correct length, same modulus as the one being extracted from
CER-File).

As I'm not a CryptoAPI-insider: could it be possible, that I forgot to
modify some parts of the CryptoAPI-MSDN examples, that could lead to
this behaviour?
Any other ideas?


Best Regards,
Johannes Resch


Code for getting RSA-Exponent and Modulus from raw Public-Key-Data:

byte[] pk = cert.GetPublicKey();
byte[] exp = new byte[3];
byte[] modulus = new byte[keyLen/8];
Array.Copy(pk, pk.Length - exp.Length, exp, 0, exp.Length);
Array.Copy(pk, pk.Length - exp.Length - 2 - modulus.Length, modulus,
0, modulus.Length);
RSAParameters rsaParams = new RSAParameters();
rsaParams.Modulus = modulus;
rsaParams.Exponent = exp;
 
M

Michel Gallant

Hi Johannes,
The Pinvoke to CryptoAPI should return the correct key size.
I just tried this for 1024, 2048 and 16,384 bit RSA keys and
they return the correct public key.

However, if you instantiate a .NET oRSA with only the
public key and exponent, the KeySize parameter is not properly
updated, but the oRSA can still be used for enveloping. I KeySize
parameter is available if you instantiate with a keycontainer specifier.

Note that the MSDN EncyrptTo.cs sample has a "Verbose" switch
to show the details of the key returned.

- Mitch Gallant

Johannes Resch said:
Hello,

I'm currently writing an application in C# to support message
integrity checks with signatures.
For this, I need to use X.509-Certificates from files (CER encoded) as
well as from the windows local machine certificate store.

I modified the CryptoAPI P/Invoke-Examples from MSDN
(http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dncapi/html/encryptdecrypt2a.asp?f
rame=true&hidetoc=true)
to access the windows local machine certificate store from C#.

This works, I can access both public and private keys of a chosen
certificate.

For getting RSA-PK-Parameters from a C# X509Certificate, I used the
way described in http://groups.google.at/groups?hl=d...-8&[email protected].
(getting the Modulus and Exponent from raw data returned from
GetPublicKey(); see code below).


However, if I take a closer look at the public key I get from the
Windows Certificate store, there are two problems:

1.) the modulus length is always 128 Byte, no matter if I use PKs with
keylengths greater than 1024 Bit.

2.) the modulus-parameter of two public keys (one extracted from
CER-File via X509Certificate and GetPublicKey(), the other one
received from the same certificate in Windows CertStore) don't match
at all (they one from windows certstore is not just truncated, it
seems to be completely different).

If I open MMC and take a look at the certificates public key in the
windows certificate-management applet, I can see the correct public
key (correct length, same modulus as the one being extracted from
CER-File).

As I'm not a CryptoAPI-insider: could it be possible, that I forgot to
modify some parts of the CryptoAPI-MSDN examples, that could lead to
this behaviour?
Any other ideas?


Best Regards,
Johannes Resch


Code for getting RSA-Exponent and Modulus from raw Public-Key-Data:

byte[] pk = cert.GetPublicKey();
byte[] exp = new byte[3];
byte[] modulus = new byte[keyLen/8];
Array.Copy(pk, pk.Length - exp.Length, exp, 0, exp.Length);
Array.Copy(pk, pk.Length - exp.Length - 2 - modulus.Length, modulus,
0, modulus.Length);
RSAParameters rsaParams = new RSAParameters();
rsaParams.Modulus = modulus;
rsaParams.Exponent = exp;
 
J

Johannes Resch

Michel Gallant said:
Hi Johannes,
The Pinvoke to CryptoAPI should return the correct key size.
I just tried this for 1024, 2048 and 16,384 bit RSA keys and
they return the correct public key.

However, if you instantiate a .NET oRSA with only the
public key and exponent, the KeySize parameter is not properly
updated, but the oRSA can still be used for enveloping. I KeySize
parameter is available if you instantiate with a keycontainer specifier.

Hi Mitch,


I think I have located the problem:

I used to call a function, which would access the windows certificate
store (with the code from your MSDN-example), initialize a
RSACryptoServiceProvider object and return this object as result.

Then I tried to export the RSAParameters from that object, which gave
wrong values (as described in my initial posting).

However, if I access the windows certificate store and then return a
X509Certificate object instead, I can extract the correct public key
parameters.


Best Regards,
Johannes Resch
 
M

Michel Gallant

Johannes Resch said:
"Michel Gallant" <[email protected]> wrote in message

Hi Mitch,


I think I have located the problem:

I used to call a function, which would access the windows certificate
store (with the code from your MSDN-example), initialize a
RSACryptoServiceProvider object and return this object as result.

Then I tried to export the RSAParameters from that object, which gave
wrong values (as described in my initial posting).

I think this has to do with fully initializing the RSAParameters before
exporting it (might be a bug in the export method).
I seem to recall something like this happening in another context.
Will check it out as time permits.
- Mitch
 
M

Michel Gallant

Johannes Resch said:
"Michel Gallant" <[email protected]> wrote in message

Hi Mitch,


I think I have located the problem:

I used to call a function, which would access the windows certificate
store (with the code from your MSDN-example), initialize a
RSACryptoServiceProvider object and return this object as result.

Then I tried to export the RSAParameters from that object, which gave
wrong values (as described in my initial posting).

However, if I access the windows certificate store and then return a
X509Certificate object instead, I can extract the correct public key
parameters.
I *can't* reproduce your results with the following code snippet. I get identical
properties for the first RSAParameters as for the exported 2nd object:

RSACryptoServiceProvider oRSA = new RSACryptoServiceProvider();
RSAParameters RSAKeyInfo = new RSAParameters();
RSAKeyInfo.Modulus = modulus; //obtained from Pinvoke parsing of store cert (or file cert)
RSAKeyInfo.Exponent = exponent; //obtained from Pinvoke parsing of store cert (or file cert)
oRSA.ImportParameters(RSAKeyInfo);

RSAParameters exportedParams = oRSA.ExportParameters(false); //export only public stuff
showBytes(exportedParams.Modulus); //identical to original cert modulus
showBytes(exportedParams.Exponent); //identical to original cert exponent

- Mitch Gallant
 
J

Johannes Resch

Hi Mitch,

Michel said:
I *can't* reproduce your results with the following code snippet. I get identical
properties for the first RSAParameters as for the exported 2nd object:

[code snippet]
- Mitch Gallant

Maybe I was a bit unclear - the way I do initialization of the
RSACryptoServiceProvider is like this (I need the private key part also
and haven't found a better way yet):

CryptoAPI cA = new CryptoAPI();
if (holder == null)
throw new ArgumentNullException("holder", "passed null argument");

if (cA.GetRecipientPVKProps(holder))
{
CspParameters cp = new CspParameters();
cp.KeyContainerName = cA.keycontainer;
cp.KeyNumber = cA.RSAkeytype;
RSACryptoServiceProvider oRSA = new RSACryptoServiceProvider(cp);
RSAParameters rsaParam = oRSA.ExportParameters(true); //for debuggging
return oRSA;
}

The "CryptoAPI" class corresponds to your class "DecryptTo", with a few
additions.

I'm currently debugging through that code. The certificate opened has a
4096 Bit PK.

If I take a look at the objects from code snippet above before the
function returns, the internal states are the following:

The "cA.recipcert" property has a public key with size of 526 byte, so
that seems to be sane for a 4096 bit public key.

However, oRSA.ALG_TYPE_RSA is 1024 and oRSA.KeyLen is 1024.
rsaParam.Modulus is 128 Byte.


Any ideas?


Best Regards,
Johannes Resch
 
M

Michel Gallant

Johannes
Are you using .NET Framework 1.0 ??
There is a bug in 1.0 whereby oRSA.KeyLen is not properly updated
(just tried this). There are some other bugs in .NET 1.0 Crypto (see clone
code in EncryptTo.cs sample :)
..NET 1.1 properly reports the correct key size.

The DecryptTo.cs sample code in the MSDN article should work for
both 1.0/1.1. The code in the DoRSADecrypt() method in that sample
does not explicitly make use of KeyLen property, and 1.0 seems to maintain the
internal state of RSACryptoServiceProvider oRSA = new RSACryptoServiceProvider(cp);
so RSA decryption based on this oRSA should work properly (I haven't had any problems in 1.0).

I tried this in .NET 1.0 and 1.1, specifically using a 4096 bit keysize (and many other
size RSA keys also). Adding the following code at the very end of Main(), I get identical
key modulus data:
........
Console.WriteLine("\n*** Failed to decrypt file ****") ;
CspParameters cp = new CspParameters();
cp.KeyContainerName = oDec.keycontainer;
cp.KeyNumber = oDec.RSAkeytype;

RSACryptoServiceProvider oRSA = new RSACryptoServiceProvider(cp);
Console.WriteLine("Key size {0} bits", oRSA.KeySize) ; //.NET 1.0 reports 1024 always (bug?)
RSAParameters rsaParams = oRSA.ExportParameters(true);
// --- Examine exported modulus ------------
Console.WriteLine("Size of modulus {0}", rsaParams.Modulus.Length) ;
Console.Write("rsaParams.Modulus = ", rsaParams.Modulus);
DisplayByteArray(rsaParams.Modulus);
} //end Main


and the exported rsaParams.Modulus.Length and the detailed modulus bytes exported
are identical to oDec.certkeymodulus property extracted by GetCertPublicKey() method
in DecryptTo.cs sample.

Cheers,
- Mitch Gallant

Johannes Resch said:
Hi Mitch,

Michel said:
I *can't* reproduce your results with the following code snippet. I get identical
properties for the first RSAParameters as for the exported 2nd object:

[code snippet]
- Mitch Gallant

Maybe I was a bit unclear - the way I do initialization of the
RSACryptoServiceProvider is like this (I need the private key part also
and haven't found a better way yet):

CryptoAPI cA = new CryptoAPI();
if (holder == null)
throw new ArgumentNullException("holder", "passed null argument");

if (cA.GetRecipientPVKProps(holder))
{
CspParameters cp = new CspParameters();
cp.KeyContainerName = cA.keycontainer;
cp.KeyNumber = cA.RSAkeytype;
RSACryptoServiceProvider oRSA = new RSACryptoServiceProvider(cp);
RSAParameters rsaParam = oRSA.ExportParameters(true); //for debuggging
return oRSA;
}

The "CryptoAPI" class corresponds to your class "DecryptTo", with a few
additions.

I'm currently debugging through that code. The certificate opened has a
4096 Bit PK.

If I take a look at the objects from code snippet above before the
function returns, the internal states are the following:

The "cA.recipcert" property has a public key with size of 526 byte, so
that seems to be sane for a 4096 bit public key.

However, oRSA.ALG_TYPE_RSA is 1024 and oRSA.KeyLen is 1024.
rsaParam.Modulus is 128 Byte.


Any ideas?


Best Regards,
Johannes Resch
 
J

Johannes Resch

Mitch,

I've got .net framework v1.1 (with german language pack v1.1) installed.

I just did again a few tests with new generated certificates with larger
keysizes (4096, 8192 bit) - still the same results (oRSA.KeySize always
1024 bit, RSAParameters exported from oRSA always have sizes corresponding
to 1024 bit keys).

So this leaves me in the situation, that no valid signatures can be
created.
The bogus private RSAParameters would be used, so the signature is
worthless (and signature verification, which is based on the correct
X509Certificates taken from a CER-file or DecryptTo.recipcert, fails).

Since I need a working solution soon, I guess I have to take the long way
and get a PRIVATEKEYBLOB exported (with all that additional steps
(Q228786) required), which I can then parse to get the correct values for
the private RSA parameters.

I remember that you had a PRIVATEKEYBLOB parser for C# on your examples
page - could you consider making just that one example available again?
This would be great.


Best Regards,
Johannes Resch

Michel said:
Are you using .NET Framework 1.0 ??
There is a bug in 1.0 whereby oRSA.KeyLen is not properly updated
(just tried this). There are some other bugs in .NET 1.0 Crypto (see clone
code in EncryptTo.cs sample :)
..NET 1.1 properly reports the correct key size.
The DecryptTo.cs sample code in the MSDN article should work for
both 1.0/1.1. The code in the DoRSADecrypt() method in that sample
does not explicitly make use of KeyLen property, and 1.0 seems to maintain the
internal state of RSACryptoServiceProvider oRSA = new RSACryptoServiceProvider(cp);
so RSA decryption based on this oRSA should work properly (I haven't had any
problems in 1.0).
I tried this in .NET 1.0 and 1.1, specifically using a 4096 bit keysize (and many other
size RSA keys also). Adding the following code at the very end of Main(), I get identical
key modulus data:
........
Console.WriteLine("n*** Failed to decrypt file ****") ;
CspParameters cp = new CspParameters();
cp.KeyContainerName = oDec.keycontainer;
cp.KeyNumber = oDec.RSAkeytype;
RSACryptoServiceProvider oRSA = new RSACryptoServiceProvider(cp);
Console.WriteLine("Key size {0} bits", oRSA.KeySize) ; //.NET 1.0 reports 1024 always (bug?)
RSAParameters rsaParams = oRSA.ExportParameters(true);
// --- Examine exported modulus ------------
Console.WriteLine("Size of modulus {0}", rsaParams.Modulus.Length) ;
Console.Write("rsaParams.Modulus = ", rsaParams.Modulus);
DisplayByteArray(rsaParams.Modulus);
} //end Main

and the exported rsaParams.Modulus.Length and the detailed modulus bytes exported
are identical to oDec.certkeymodulus property extracted by GetCertPublicKey() method
in DecryptTo.cs sample.
Cheers,
- Mitch Gallant
Hi Mitch,

Michel said:
I *can't* reproduce your results with the following code snippet. I get identical
properties for the first RSAParameters as for the exported 2nd object:

[code snippet]
- Mitch Gallant

Maybe I was a bit unclear - the way I do initialization of the
RSACryptoServiceProvider is like this (I need the private key part also
and haven't found a better way yet):

CryptoAPI cA = new CryptoAPI();
if (holder == null)
throw new ArgumentNullException("holder", "passed null argument");

if (cA.GetRecipientPVKProps(holder))
{
CspParameters cp = new CspParameters();
cp.KeyContainerName = cA.keycontainer;
cp.KeyNumber = cA.RSAkeytype;
RSACryptoServiceProvider oRSA = new RSACryptoServiceProvider(cp);
RSAParameters rsaParam = oRSA.ExportParameters(true); //for debuggging
return oRSA;
}

The "CryptoAPI" class corresponds to your class "DecryptTo", with a few
additions.

I'm currently debugging through that code. The certificate opened has a
4096 Bit PK.

If I take a look at the objects from code snippet above before the
function returns, the internal states are the following:

The "cA.recipcert" property has a public key with size of 526 byte, so
that seems to be sane for a 4096 bit public key.

However, oRSA.ALG_TYPE_RSA is 1024 and oRSA.KeyLen is 1024.
rsaParam.Modulus is 128 Byte.


Any ideas?


Best Regards,
Johannes Resch
 
J

Johannes Resch

Finally, I decided to use the Mentalis.org Security Library - quite an
overkill for that single function I needed but signing works now.



Johannes said:
I've got .net framework v1.1 (with german language pack v1.1) installed.
I just did again a few tests with new generated certificates with larger
keysizes (4096, 8192 bit) - still the same results (oRSA.KeySize always
1024 bit, RSAParameters exported from oRSA always have sizes corresponding
to 1024 bit keys).
So this leaves me in the situation, that no valid signatures can be
created.
The bogus private RSAParameters would be used, so the signature is
worthless (and signature verification, which is based on the correct
X509Certificates taken from a CER-file or DecryptTo.recipcert, fails).
Since I need a working solution soon, I guess I have to take the long way
and get a PRIVATEKEYBLOB exported (with all that additional steps
(Q228786) required), which I can then parse to get the correct values for
the private RSA parameters.
I remember that you had a PRIVATEKEYBLOB parser for C# on your examples
page - could you consider making just that one example available again?
This would be great.

Best Regards,
Johannes Resch
Michel said:
Are you using .NET Framework 1.0 ??
There is a bug in 1.0 whereby oRSA.KeyLen is not properly updated
(just tried this). There are some other bugs in .NET 1.0 Crypto (see clone
code in EncryptTo.cs sample :)
..NET 1.1 properly reports the correct key size.
The DecryptTo.cs sample code in the MSDN article should work for
both 1.0/1.1. The code in the DoRSADecrypt() method in that sample
does not explicitly make use of KeyLen property, and 1.0 seems to maintain the
internal state of RSACryptoServiceProvider oRSA = new RSACryptoServiceProvider(cp);
so RSA decryption based on this oRSA should work properly (I haven't had
any
problems in 1.0).
I tried this in .NET 1.0 and 1.1, specifically using a 4096 bit keysize
(and
many other
size RSA keys also). Adding the following code at the very end of Main(), I get identical
key modulus data:
........
Console.WriteLine("n*** Failed to decrypt file ****") ;
CspParameters cp = new CspParameters();
cp.KeyContainerName = oDec.keycontainer;
cp.KeyNumber = oDec.RSAkeytype;
RSACryptoServiceProvider oRSA = new RSACryptoServiceProvider(cp);
Console.WriteLine("Key size {0} bits", oRSA.KeySize) ; //.NET 1.0 reports 1024 always (bug?)
RSAParameters rsaParams = oRSA.ExportParameters(true);
// --- Examine exported modulus ------------
Console.WriteLine("Size of modulus {0}", rsaParams.Modulus.Length) ;
Console.Write("rsaParams.Modulus = ", rsaParams.Modulus);
DisplayByteArray(rsaParams.Modulus);
} //end Main
and the exported rsaParams.Modulus.Length and the detailed modulus bytes exported
are identical to oDec.certkeymodulus property extracted by GetCertPublicKey() method
in DecryptTo.cs sample.
Cheers,
- Mitch Gallant
Hi Mitch,

Michel Gallant wrote:

I *can't* reproduce your results with the following code snippet. I get
identical
properties for the first RSAParameters as for the exported 2nd object:

[code snippet]

- Mitch Gallant

Maybe I was a bit unclear - the way I do initialization of the
RSACryptoServiceProvider is like this (I need the private key part also
and haven't found a better way yet):

CryptoAPI cA = new CryptoAPI();
if (holder == null)
throw new ArgumentNullException("holder", "passed null argument");

if (cA.GetRecipientPVKProps(holder))
{
CspParameters cp = new CspParameters();
cp.KeyContainerName = cA.keycontainer;
cp.KeyNumber = cA.RSAkeytype;
RSACryptoServiceProvider oRSA = new RSACryptoServiceProvider(cp);
RSAParameters rsaParam = oRSA.ExportParameters(true); //for debuggging
return oRSA;
}

The "CryptoAPI" class corresponds to your class "DecryptTo", with a few
additions.

I'm currently debugging through that code. The certificate opened has a
4096 Bit PK.

If I take a look at the objects from code snippet above before the
function returns, the internal states are the following:

The "cA.recipcert" property has a public key with size of 526 byte, so
that seems to be sane for a 4096 bit public key.

However, oRSA.ALG_TYPE_RSA is 1024 and oRSA.KeyLen is 1024.
rsaParam.Modulus is 128 Byte.


Any ideas?


Best Regards,
Johannes Resch
 
M

Michel Gallant

Not sure what the problem is then.
Just to make sure I understand ..
IF you use the DecryptTo.cs code form the msdn article EXACTLY as is
(i.e. to modifications, no exports, only with CU stores) do you have
any problems?

The privatekeyblob parser won't be of use (exactly the same code essentially
in MSDN article) and it Pinvokes to extract the key credentials .. which you
indicated works properly. Your problem seems to be associated with some
internat state in .NET marshaling (there are other known problems as I mentioned
before in thread).

What OS are you using?
Does anyone else have problems with the EncryptTo/DecyrptTo code AS IS
at:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dncapi/html/encryptdecrypt2a.asp

- Mitch Gallant

Johannes Resch said:
Mitch,

I've got .net framework v1.1 (with german language pack v1.1) installed.

I just did again a few tests with new generated certificates with larger
keysizes (4096, 8192 bit) - still the same results (oRSA.KeySize always
1024 bit, RSAParameters exported from oRSA always have sizes corresponding
to 1024 bit keys).
-- snip
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top