Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# - Verify client certificate signed by proper root

I know there are a lot of questions on this argument but I'm stuck into this for several days now so here I am. I've got a root certificate and a client certificate. I need to replicate in a C# web API project what the command openssl verify -CAfile ca.pem client.pem does.

This is what i know for now (hope it's actually true):

  • Verify() method actually validate that the certificate is signed by an authority. It's like a format control. It doesn't matter which autority signed the certificate.
  • X509 Chain is the way to go. Add your ca certificate inside an extra store because i'm not going to install the certificate into Windows. Then build passing the client certificate. Let's the magic happens! Unfortunately I've got some problems with the configuration.

Let me be more clear with an example

private bool VerifyCertificate(X509Certificate2 client)
{
    X509Chain chain = new X509Chain();
    var stringCert = WebConfigurationManager.AppSettings["CACertificate"];
    var byteCert = Encoding.ASCII.GetBytes(stringCert);
    var authority = new X509Certificate2(byteCert);

    chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
    chain.ChainPolicy.VerificationFlags = X509VerificationFlags.IgnoreWrongUsage;

    chain.ChainPolicy.ExtraStore.Add(authority);

    // Do the preliminary validation.
    if (!chain.Build(client))
        return false;

    return true;
}

With this example the program returns false. The build is not passed. I'm sure the problem is with the ChainPolicy properties so i tried a different configuration

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

But this one is not going to verify anything, in fact using my ca cert the method returns true and using another ca certificate (which i didn't use to signed my client certificate) the method also returns true.

I Searched for an OpenSSL wrapper for C# and i found it but unfortunately is based on old libraries and the repo isn't manteined anymore. Also, i would achieve my goal using just .net framework if possible.

So guys, fast recap. I want to check that only the certificate that is firmed by my ca certificate can pass the validation, all the others must be stopped.

Thanks in advance for any help

like image 368
BlackShawarna Avatar asked Mar 02 '26 00:03

BlackShawarna


1 Answers

ExtraStore isn't limiting, it provides extra certificates to help complete the chain. It provides no trust data.

In order to determine if the certificate is issued by the CA that you want you need to do something like:

private static readonly X509Certificate2 s_trustedRoot = ObtainTheRoot();
private static readonly byte[] s_normalizedRoot = s_trustedRoot.RawData;

private bool VerifyCertificate(X509Certificate2 candidate)
{
    X509Chain chain = new X509Chain();
    // set all the things you need to set to make it build

    if (!chain.Build(candidate))
        return false;

    // Check that the root certificate was the expected one.
    X509ChainElementCollection elements = chain.ChainElements;
    return elements[elements.Count - 1].Certificate.RawData.SequenceEqual(s_normalizedRoot);
}

I promoted the cert and normalized byte form of it to statics on the assumption that they don't change once the process starts. If the cert can change dynamically then you should adjust accordingly.

like image 189
bartonjs Avatar answered Mar 04 '26 13:03

bartonjs