HTTPS and X509 certificates in .NET Part 4: working with certificates in code

Introduction

In the previous post we successfully installed our self-signed CA certificate in the Trusted Root CA folder. We also installed a derived certificate in the Personal certificates folder. We then saw how to make IIS use our certificate for a secured web site.

In this post we’ll start looking into the certificate-related classes in .NET.

Loading a certificate from a file

Digital certificates are represented by the X509Certificate2 class in .NET located in the System.Security.Cryptography.X509Certificates namespace. The class has numerous overloaded constructors. You can pass in the file path to the certificate, a byte array content, a password etc. Let’s see whether we can load the CA certificate we created before in this series:

X509Certificate2 certificate = new X509Certificate2(@"C:\TestProjects\Certificates\Certificates\RootCert.cer");
					
string expirationDate = certificate.GetExpirationDateString();
string issuer = certificate.Issuer;			
string effectiveDateString = certificate.GetEffectiveDateString();
string nameInfo = certificate.GetNameInfo(X509NameType.SimpleName, true);
bool hasPrivateKey = certificate.HasPrivateKey;
			
Debug.WriteLine(expirationDate);
Debug.WriteLine(issuer);
Debug.WriteLine(effectiveDateString);
Debug.WriteLine(nameInfo);
Debug.WriteLine(hasPrivateKey);

The code prints the following values:

2029-12-31 23:00:00
CN=RootCert
2014-12-31 23:00:00
RootCert
False

We can also load pfx files:

X509Certificate2 certificate = new X509Certificate2(@"C:\TestProjects\Certificates\Certificates\mylocalsite.local.pfx");

The HasPrivateKey property will be True now as the pfx file includes the private key as well. The X509Certificate2 class also has an Export method with various overloads to transform it into a byte array.

Loading a certificate from the store

If you want to inspect a certificate that’s already installed in the Windows certificate store then it’s easier to use the X509Store class. The X509Store class has an overload where you can specify the store location and the store name. Store location will be either User Store or Computer Store. The store names map to the folder names in the certificates snap-in GUI:

Store names as folders in MMC certificates snap-in

You can provide the store name either as a string or as an enumeration. The enumeration includes the built-in store locations such as Personal and Trusted Root CA. You’ll see some non-standard store locations in the printscreen above, like “testCertStore”. Obviously there won’t be any entry for custom locations in the StoreName enumeration.

It’s not always straightforward to find the exact mapping between the folder names in the GUI and the StoreName enumeration values. E.g. StoreName.CertificateAuthority maps to “Intermediate Certification Authorities” in the GUI and not the “Trusted Root Certification Authorities”. Trusted CAs are represented by StoreName.Root. Also, the “Personal” folder is represented by StoreName.My.

The following code will open the trusted CA store on the local machine and enumerate through the available certificates. The store must be closed as well at the end:

X509Store computerCaStore = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
			
try
{
	computerCaStore.Open(OpenFlags.ReadOnly);
	X509Certificate2Collection certificatesInStore = computerCaStore.Certificates;
	foreach (X509Certificate2 cert in certificatesInStore)
	{
		Debug.WriteLine(cert.GetExpirationDateString());
		Debug.WriteLine(cert.Issuer);
		Debug.WriteLine(cert.GetEffectiveDateString());
		Debug.WriteLine(cert.GetNameInfo(X509NameType.SimpleName, true));
		Debug.WriteLine(cert.HasPrivateKey);
                Debug.WriteLine(cert.SubjectName.Name);
		Debug.WriteLine("-----------------------------------");
	}
}
finally
{
	computerCaStore.Close();
}

Our self-signed CA will also be listed:

2029-12-31 23:00:00
CN=RootCert
2014-12-31 23:00:00
RootCert
False
CN=RootCert

Let’s do the same for the Personal store name:

X509Store computerCaStore = new X509Store(StoreName.My, StoreLocation.LocalMachine);

The derived certificate is also found:

2029-12-31 23:00:00
CN=RootCert
2014-12-31 23:00:00
RootCert
True
CN=mylocalsite.local

Searching for certificates

The X509Certificate2Collection object has a Find method where you can search for specific certificates by a number of criteria. The exact search type is defined by the X509FindType enumeration. The following example will locate all certificates whose issuer name is equal to “RootCert”:

X509Certificate2Collection findResult = certificatesInStore.Find(X509FindType.FindByIssuerName, "RootCert", false);

…and this is how you can find a certificate by its subject name:

X509Certificate2Collection findResult = certificatesInStore.Find(X509FindType.FindBySubjectName, "mylocalsite.local", false);

The boolean parameter indicates that we want to look at all certificates and not only the valid ones.

Installing and deleting certificates

The X509Store class provides methods to install and delete certificates as well. I’ll quickly create a new CA root and a derived certificate to test the Add and Remove methods of X509Store using the makecert tool like we did before:

makecert.exe -r -n “CN=RootCertReloaded” -pe -sv RootCertReloaded.pvk -a sha1 -len 2048 -b 01/01/2015 -e 01/01/2030 -cy authority RootCertReloaded.cer
pvk2pfx.exe -pvk RootCertReloaded.pvk -spc RootCertReloaded.cer -pfx RootCertReloaded.pfx
makecert.exe -ic RootCertReloaded.cer -iv RootCertReloaded.pvk -pe -sv greatsite.com.pvk -a sha1 -n “CN=greatsite.com” -len 2048 -b 01/01/2015 -e 01/01/2030 -sky exchange greatsite.com.cer -eku 1.3.6.1.5.5.7.3.1
pvk2pfx.exe -pvk greatsite.com.pvk -spc greatsite.com.cer -pfx greatsite.com.pfx

I ignored the password fields and clicked “None” to simplify the process.

Note that you’ll need to run Visual Studio with administrator rights otherwise the code will fail.

This is how the CA certificate can be installed:

X509Store computerCaStore = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
X509Certificate2 rootCert = new X509Certificate2(@"C:\TestProjects\Certificates\DummyCerts\RootCertReloaded.cer");
try
{
	computerCaStore.Open(OpenFlags.ReadWrite);
	computerCaStore.Add(rootCert);
}
catch (Exception ex)
{
	Debug.WriteLine("Root certificate import failed: " + ex.Message);
}
finally
{
	computerCaStore.Close();
}

Refresh the certificates snap-in in the MMC GUI and the RootCertReloaded certificate should be visible:

CA certificate installed in code

The code to install the derived certificate pfx file is almost identical:

X509Store personalStore = new X509Store(StoreName.My, StoreLocation.LocalMachine);
X509Certificate2 sslCert = new X509Certificate2(@"C:\TestProjects\Certificates\DummyCerts\greatsite.com.pfx");
try
{
	personalStore.Open(OpenFlags.ReadWrite);
	personalStore.Add(sslCert);
}
catch (Exception ex)
{
	Debug.WriteLine("SSL certificate import failed: " + ex.Message);
}
finally
{
	personalStore.Close();
}

The derived certificate is visible in the Personal store after a refresh:

Derived certificate installed in code

The uninstallation process is very similar. We’ll need to locate the certificate in the appropriate store and call the Remove method of X509Store:

X509Store personalStore = new X509Store(StoreName.My, StoreLocation.LocalMachine);
try
{
	personalStore.Open(OpenFlags.ReadWrite);
	X509Certificate2Collection findResult = personalStore.Certificates.Find(X509FindType.FindBySubjectName, "greatsite.com", false);
	if (findResult.Count > 0)
	{
		foreach (X509Certificate2 item in findResult)
		{
			personalStore.Remove(item);
		}
	}
}
catch (Exception ex)
{
	Debug.WriteLine("SSL certificate import failed: " + ex.Message);
}
finally
{
	personalStore.Close();
}

X509Store computerCaStore = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
try
{
	computerCaStore.Open(OpenFlags.ReadWrite);
	X509Certificate2Collection findResult = computerCaStore.Certificates.Find(X509FindType.FindBySubjectName, "RootCertReloaded", false);
	if (findResult.Count > 0)
	{
		foreach (X509Certificate2 item in findResult)
		{
			computerCaStore.Remove(item);
		}
	}
}
catch (Exception ex)
{
	Debug.WriteLine("Root certificate uninstall failed: " + ex.Message);
}
finally
{
	computerCaStore.Close();
}

Refresh the GUI and the CA and derived certificates should be gone from the list.

In the next post we’ll see how to validate certificates in code.

You can view the list of posts on Security and Cryptography here.

Advertisements

About Andras Nemes
I'm a .NET/Java developer living and working in Stockholm, Sweden.

6 Responses to HTTPS and X509 certificates in .NET Part 4: working with certificates in code

  1. Steve Bower says:

    This is very helpful, neat intro into HTTPS and certificates. Do you happen to have any links to getting certificates from the personal store using Python?

  2. Kornel says:

    Useful post thanks mate! Let me ask that would be possible to force popping up the certificate selection window from the server (asp.net mvc) for example when a user pushes a button? I’d like to get 2nd factor (smart card) auth, even after login at a certain point of the use case. I just want to avoid burdening users on login since the 2nd factor need rarely.

  3. Dorothy says:

    Nicely written post! I tried your code and it returned 52 out of my 53 certs? And of course, the one I need is missing. I am new to working with certificates. Do you know why that one doesn’t return?

    X509Store computerCaStore = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
    computerCaStore.Open(OpenFlags.ReadOnly);
    X509Certificate2Collection certificatesInStore = computerCaStore.Certificates;

  4. Pingback: X.509 certificates in .NET | Around computing

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

ultimatemindsettoday

A great WordPress.com site

Elliot Balynn's Blog

A directory of wonderful thoughts

Robin Sedlaczek's Blog

Developer on Microsoft Technologies

HarsH ReaLiTy

A Good Blog is Hard to Find

Softwarearchitektur in der Praxis

Wissenswertes zu Webentwicklung, Domain-Driven Design und Microservices

the software architecture

thoughts, ideas, diagrams,enterprise code, design pattern , solution designs

Technology Talks

on Microsoft technologies, Web, Android and others

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

Anything around ASP.NET MVC,WEB API, WCF, Entity Framework & AngularJS

Cyber Matters

Bite-size insight on Cyber Security for the not too technical.

Guru N Guns's

OneSolution To dOTnET.

Johnny Zraiby

Measuring programming progress by lines of code is like measuring aircraft building progress by weight.

%d bloggers like this: