While trying to extract the public key from a certificate you may get an exception that says: 'System.Security.Cryptography.CryptographicException - The index value is not valid'.

The exact error is CRYPT_E_INVALID_INDEX which means "The index value is not valid".

This happens if you try to get the public key using X509Certificate2::GetPublicKey method and the reason is you cannot use this method to get the public key. It has to be in a specific format.

First get a handle to an RSACryptoServiceProvider from the certificates public key and then export the CSP blob.

Example:

RSACryptoServiceProvider PublicKey;
 
PublicKey = (RSACryptoServiceProvider)Cert.PublicKey.Key;
 
byte[] pbPublicKeyBlob = PublicKey.ExportCspBlob(false);
 
A public key blob is defined as:
 
BLOBHEADER blobheader;
 
RSAPUBKEY rsapubkey;
 
BYTE modulus[rsapubkey.bitlen / 8];

If you refer to the MSDN link http://msdn.microsoft.com/en-us/library/dd388945(VS.85).aspx, you will notice a function GetPublicKeyFromCertificate. This function GetPublicKeyFromCertificate expects the same blob minus the BLOBHEADER. This is the PUBLIC_KEY_VALUES structure that is defined as:

 
typedef PUBLIC_KEY_VALUES struct
{
 RSAPUBKEY rsapubkey;
 BYTE modulus[rsapubkey.bitlen / 8];
}

To get the public key from the certificate you can use the SignedCms class as shown below.

Here in this code I am looking for version 3 certs that have no CA or extension. The BLOB actually had a certificate chain.

 
X509Certificate2 Cert = new X509Certificate2();
X509BasicConstraintsExtension BasicConstraints = new 
X509BasicConstraintsExtension();
string Oid = BasicConstraints.Oid.Value;
SignedCms Pkcs7 = new SignedCms();
Pkcs7.Decode(TestCert);
for (int n = 0; n < Pkcs7.Certificates.Count; n++)
{
if ((Pkcs7.Certificates[n].Version == 3) && (null == 
Pkcs7.Certificates[n].Extensions[Oid]))
{
Cert = Pkcs7.Certificates[n];
break;
}
else if((Pkcs7.Certificates[n].Version == 3))
{
BasicConstraints = 
(X509BasicConstraintsExtension)Pkcs7.Certificates[n].Extensions[Oid];
if (BasicConstraints.CertificateAuthority == false)
{
Cert = Pkcs7.Certificates[n];
break;
}
}
}

Once you get the certificate you can use the code stated below to get the public key.

 
RSACryptoServiceProvider PublicKey;
PublicKey = (RSACryptoServiceProvider)Cert.PublicKey.Key;
byte[] pbPublicKeyBlob = PublicKey.ExportCspBlob(false);

 

Shamik Misra