Using client certificates in .NET part 4: working with client certificates in code

Introduction

In the previous post we discussed how to install certificates into the certificate store. We looked at the tool mmc.exe and its certificate handler snap-in. We also inspected the imported certificates visually and verified that the client certificate is valid.

In this post we’ll see a couple of examples how to work with client certificates in code.

Digital certificates in code

We took up this topic before in two separate posts in the series dedicated to SSL certificates:

Most of that material is directly applicable to client certificates as well. Therefore this post only takes up the most important operations that you’ll likely encounter when working with certificates in your .NET projects. You’re advised to read them for more details on how to load, install, uninstall and verify certificates using C#.

Loading a certificate from the store

If you want to inspect a certificate that’s already installed in the Windows certificate store then you can use the X509Store class in the System.Security.Cryptography.X509Certificates namespace. 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 type enumeration in the mmc certificate 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 userCaStore = new X509Store(StoreName.Root, StoreLocation.CurrentUser);

try
{
	userCaStore.Open(OpenFlags.ReadOnly);
	X509Certificate2Collection certificatesInStore = userCaStore.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
{
	userCaStore.Close();
}

Our self-signed CA will also be listed:

2030-01-01 00:00:00
CN=RootCertificate
2015-01-01 00:00:00
RootCertificate
False
CN=RootCertificate

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

X509Store userCaStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);

The derived client certificate is also found:

2030-01-01 00:00:00
CN=RootCertificate
2015-01-01 00:00:00
RootCertificate
True
CN=localtestclientcert

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 “RootCertificate”:

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

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

X509Certificate2Collection findResult = certificatesInStore.Find(X509FindType.FindBySubjectName, "localtestclientcert", true);

The boolean parameter indicates that we want to look at valid certificates only.

Certificate validation in C#

The two most important objects in .NET that will help you validate a certificate are X509Chain and X509ChainPolicy.

The X509Chain object represents the chain of trust when checking the validity of a certificate. X509ChainPolicy fine-tunes how you’d like to validate the certificate, i.e. which criteria the chain of trust should fulfil.

Let’s see how our client certificate we created before can be validated. Consider the following sample code:

private static void RunClientCertValidation()
{
	X509Store userCaStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);

	try
	{
		userCaStore.Open(OpenFlags.ReadOnly);
		X509Certificate2Collection certificatesInStore = userCaStore.Certificates;
		X509Certificate2Collection findResult = certificatesInStore.Find(X509FindType.FindBySubjectName, "localtestclientcert", true);				
		foreach (X509Certificate2 cert in findResult)
		{
			X509Chain chain = new X509Chain();
			X509ChainPolicy chainPolicy = new X509ChainPolicy()
			{
				RevocationMode = X509RevocationMode.Online,
				RevocationFlag = X509RevocationFlag.EntireChain
			};
			chain.ChainPolicy = chainPolicy;
			if (!chain.Build(cert))
			{
				foreach (X509ChainElement chainElement in chain.ChainElements)
				{
					foreach (X509ChainStatus chainStatus in chainElement.ChainElementStatus)
					{
						Debug.WriteLine(chainStatus.StatusInformation);
					}
				}
			}
		}
	}
	finally
	{
		userCaStore.Close();
	}
}

You’ll recognise the part of this code where we open the certificate store and load the client certificate. The validation part of the code starts within the foreach loop by constructing an X509Chain object. We then build a policy for the chain by specifying a couple of properties.

The RevokationMode enumeration will define whether or not we want to check the revocation list for this certificate: check it on-line, off-line or not at all. That’s right, client certificates can also be revoked just like “normal” SSL certificates. Let’s recall what a revocation list is:

The Certificate Revocation List (CRL) is an important term you’ll come across while working with certificates. The idea is that if a certificate needs to be revoked, e.g. in case it was compromised, then the certificate owner can inform the CA that issued it. The CA then puts the certificate on its CRL. Therefore it is the CA that handles the CRL for the certificates that it has issued. A revoked certificate cannot of course be used for secure communication.

The X509RevocationFlag will specify if we want to check the revocation list with or without the CA root. The X509ChainPolicy object has some more properties that will let you fine-grain the chain policy. The VerificationFlags enumeration will let you switch off parts of the chain, e.g.:

VerificationFlags = X509VerificationFlags.IgnoreInvalidName | X509VerificationFlags.IgnoreNotTimeValid

Normally you’d want to verify all properties so you’ll leave VerificationFlags untouched.

We then want to build the chain by calling the Build method with the certificate. If Build returns false then we know that something isn’t correct with the validation chain. So for each element in the chain elements we want to check the element status messages.

If you run the above code then you should get a validation error:

“The revocation function was unable to check revocation for the certificate.”

That’s because we specified that the online revocation list should be checked. However, there’s no revocation list anywhere on Earth that knows about our self-signed certificate. If we change the code to…

X509ChainPolicy chainPolicy = new X509ChainPolicy()
{
	RevocationMode = X509RevocationMode.NoCheck,
        RevocationFlag = X509RevocationFlag.EntireChain
};

…then the Build method passes as everything is fine with the certificate.

A more compact solution

The above code lets you fine-grain your chain building logic. There are specialised X509 validators in .NET that provide a more compact way of validating a certificate. Add the following references to your project:

References for X509 validators

The below example will build a validator that validates the chain of trust with all the validation flags turned on:

X509CertificateValidator chainTrustValidator = X509CertificateValidator.ChainTrust;
try
{
	chainTrustValidator.Validate(cert);
}
catch (Exception ex)
{
	Debug.WriteLine(ex.Message);
}

The Validate method will throw an exception if the validation fails.

In the next post we’ll take a look at how to work with certificates in a .NET MVC/Web API project.

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.

One Response to Using client certificates in .NET part 4: working with client certificates in code

  1. Prafful Srivastava says:

    Hi Andras, I want to read client X509Certificate2 in my Asp.net webapplication SO that end users can sign the server pdfs. I am getting this functionality on my local system but after hosting it on IIS ,this is not working.
    Kindlly help.

    Thanks in Advance!!!

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

iReadable { }

.NET Tips & Tricks

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: