23

I have a Windows certification authority that I am using to issue client authentication certificates via .net / c#. I have been able to successfully get it to issue certificates programmatically by calling the certification authority's API through COM. I issue a new certificate when I set up a client.

At runtime, these clients attach the certificates to requests to my server. How can I verify programmatically that an X509Certificate2 was signed by the root certificate of my certificate authority (and reject certificates signed by any other source)?

tshepang
  • 12,111
  • 21
  • 91
  • 136
Jeffrey Meyer
  • 5,410
  • 7
  • 30
  • 27

3 Answers3

41

I've done this a lot. Here's some easy code you can use.

The part in the if (!isChainValid) block is to make a pretty error message. You don't have to use that if you don't want, but you should throw an error if the chain cannot be built. The chain elements are necessary to check for your root.

X509Certificate2 authority = GetAuthorityCertificate();
X509Certificate2 certificateToValidate = GetCertificateToValidate();

X509Chain chain = new X509Chain();
chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
chain.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot;
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
chain.ChainPolicy.VerificationTime = DateTime.Now;
chain.ChainPolicy.UrlRetrievalTimeout = new TimeSpan(0, 0, 0);

// This part is very important. You're adding your known root here.
// It doesn't have to be in the computer store at all. Neither certificates do.
chain.ChainPolicy.ExtraStore.Add(authority);

bool isChainValid = chain.Build(certificateToValidate);

if (!isChainValid)
{
    string[] errors = chain.ChainStatus
        .Select(x => String.Format("{0} ({1})", x.StatusInformation.Trim(), x.Status))
        .ToArray();
    string certificateErrorsString = "Unknown errors.";

    if (errors != null && errors.Length > 0)
    {
        certificateErrorsString = String.Join(", ", errors);
    }

    throw new Exception("Trust chain did not complete to the known authority anchor. Errors: " + certificateErrorsString);
}

// This piece makes sure it actually matches your known root
var valid = chain.ChainElements
    .Cast<X509ChainElement>()
    .Any(x => x.Certificate.Thumbprint == authority.Thumbprint);

if (!valid)
{
    throw new Exception("Trust chain did not complete to the known authority anchor. Thumbprints did not match.");
}
Chris Benard
  • 3,167
  • 2
  • 29
  • 35
  • @HelloWorld Well if I was writing this today, I'd just use `if (errors?.Length > 0)` anyway. :) C# 6. Good to know; I didn't delve into the ToArray() implementation to see what happens if `Select()`'s `IEnumerable` returns no results. – Chris Benard Jan 25 '17 at 14:18
  • @ChrisBenard Is the code for checking that the certificate matches your known root really OK? That piece of code should match doing something like this and nearly always return true: `chain.ChainElements.Cast().All(x => x.Certificate.Thumbprint != "XX");` – Ogglas Nov 17 '17 at 22:00
  • @Ogglas “Any true” is not the same as “all false”. The code is right. – Chris Benard Nov 19 '17 at 00:20
  • Even if I don't do this: `chain.ChainPolicy.ExtraStore.Add(authority);`, `chain.Build(certificateToValidate)` returns true. Is this expected? (the later thumbprint validation still resolves to false) – Mattias Nordqvist Mar 20 '18 at 17:56
  • 1
    @MattiasNordqvist If the certs are OK (not expired/revoked/etc) it will be true due to the usage of `X509VerificationFlags.AllowUnknownCertificateAuthority`. The later check by thumbprint is necessary to check for the authority you've placed in the chain with `ExtraStore.Add()`. – Chris Benard Mar 21 '18 at 19:31
  • 1
    I have root and intermediate certificates (signed by root), and need to verify client certificate which is signed by an intermediate. In this case, do I need to add root and intermediate in ExtraStore, and need to check thumbprint with which certificate? – Varsh Aug 07 '20 at 07:59
  • 1
    @Varsh sounds like you do need to add root and intermediates and check the thumbprint of the root. – Chris Benard Aug 08 '20 at 16:49
  • Thank you, and my certificates are not installed in the system store, rather they are store as a `pem` file somewhere. In that case, this code is okay or secure to use? – Varsh Aug 10 '20 at 02:37
  • @Varsh my code doesn't assume anything is installed in a store. See this answer for how to load PEM data: https://stackoverflow.com/a/7748491/448 – Chris Benard Aug 11 '20 at 13:56
  • So I need to have all intermediates added, and if I only have root certificate I'll not be able to check? – Guilherme de Jesus Santos Apr 09 '21 at 13:26
  • You either have to add the intermediates or they have to be in your store to check. There's no magical way for it to check the chain without the elements in the chain. – Chris Benard Apr 13 '21 at 19:36
1

You can also use the built in method Verify() for X509Certificate2.

X509Certificate2 certificateToValidate = GetCertificateToValidate();
bool valid = certificateToValidate.Verify()

https://msdn.microsoft.com/en-us/library/system.security.cryptography.x509certificates.x509certificate2.verify.aspx

Ogglas
  • 62,132
  • 37
  • 328
  • 418
0

If you say you have a root (which is self-signed) certificate, then your only option is to keep this root certificate available on your server (without the private key of course) and perform certificate validation procedure against your root certificate. This is a mirrored situation to the web client validating server certificate chain.

Eugene Mayevski 'Callback
  • 45,135
  • 8
  • 71
  • 121
  • 1
    I have exactly that scenario. I'll clarify my question, but I am looking for the particular code necessary to validate the signing in c#. – Jeffrey Meyer Jun 27 '11 at 20:36
  • @Jeffrey I know how this is done in our SecureBlackbox for .NET, but not how it's done pure .NET Framework. – Eugene Mayevski 'Callback Jun 27 '11 at 20:41
  • There might be a better way but check out the [X509Chain](http://msdn.microsoft.com/en-us/library/system.security.cryptography.x509certificates.x509chain.aspx) class. The documentation example shows code to walk down the chain examining each element as you go. – Frank Boyne Jun 27 '11 at 21:21