Pete Maroun
Pete Maroun

Reputation: 2075

how to load password protected certificates from the X509Store?

I am building an ACS protected Azure WCF service that will require clients to authenticate via a certificate.

I would like the client (and the server) to load their respective password certs from the X509Store instead of from the file system.

I am using this code:

private static X509Certificate2 GetCertificate(string thumbprint)
{
    var certStore = new X509Store(StoreName.My, StoreLocation.LocalMachine);
    certStore.Open(OpenFlags.ReadOnly);

    X509Certificate2Collection certCollection = certStore.Certificates.Find(
        X509FindType.FindByThumbprint,
        thumbprint, false);

    certStore.Close();

    if (certCollection.Count == 0)
    {
        throw new System.Security.SecurityException(string.Format(CultureInfo.InvariantCulture, "No certificate was found for thumbprint {0}", thumbprint));
    }

    return certCollection[0]; 
}

Problem is, it's not loading the private key which it needs for authentication. I have tried to modify the return statement to this:

return new X509Certificate2(certCollection[0].Export(X509ContentType.Pfx, "password"));

However, this fails with a CryptographicException "The spcecified network password is incorrect".

Edit: The .Export() method works properly if you don't pass the password argument in.

Any help on this?

Upvotes: 26

Views: 19814

Answers (4)

Julie
Julie

Reputation: 1

I experienced this same issue, and whilst all the answers here were helpful to provide context, they were not able to solve the issue.

A password protected certificate (pfx) once loaded into the Certificate Store does not require a password to access the file.

If you can confirm that the certificate has a private key, but fail to retrieve the value, it is most likely a permissions issue.

Accessing the private key

X509Store x509Store = new X509Store(storeName, storeLocation);
x509Store.Open(OpenFlags.ReadOnly); 

X509Certificate2Collection certificates = x509Store.Certificates.Find(
    X509FindType.FindByThumbprint, 
    thumbPrint, 
    false); 
var cert =  certificates[0];  


 if (cert.HasPrivateKey)
 {
   var privateKey = cert.GetRSAPrivateKey(); 

   if (privateKey == null) 
   { //This is a permissions issue, see the next section. } 
 }

Setting Up Permissions

Consider how the application is accessing the certificate from the store. In my case, it was a website hosted on IIS so I needed to provide permissions to the IIS app pool.

  1. Locate your certificate in the certificate store
  2. R-click on cert "All Tasks" -> "Manage Private Keys"
  3. Under Group or under names: click "Add..."
  4. In the "Enter the object names to select" field, enter the identity to provide access permission, (in my case: IIS AppPool<ApplicationPoolName>)
  5. Click "Check Names" to verify that the identity is valid.
  6. Click "OK" to add the identity to the access control list.
  7. Ensure to check "Allow" next to "Read".
  8. Click "Apply" and then "OK" to save the changes.

Upvotes: 0

Darrell
Darrell

Reputation: 2175

When the cert was imported into the certificate store, I think the key has to be marked as "exportable" otherwise I don't think you can export the private key..

enter image description here

Upvotes: 0

Pete Maroun
Pete Maroun

Reputation: 2075

I used the Export without the 'password' parameter and it worked without issue.

Upvotes: 2

Peter Ritchie
Peter Ritchie

Reputation: 35881

When you export, the password you provide is the password you want to use for the exported file, it's not the password for the source certificate.

I'm not sure what you can do with X509Store and password-protected certs because the password should be supplied to the X509Certificate constructor and you get already-instantiated objects out of the store.

I think you can just get the raw data from the cert you want and construct a new one with the password you want. For example:

X509Certificate2 cert = new X509Certificate2(certCollection[0].GetRawCertData, password);

I would also suggest you try to use SecureString when dealing with passwords (but that's a different bag of worms...)

Upvotes: 11

Related Questions