Coder Social home page Coder Social logo

dotnet-x509-certificate-verification's Introduction

Validating X.509 Certificates using the .NET APIs

Validating a certificate in .NET can be done with the help of the X509Chain.Build() method, which returns a boolean value indicating if a certificate under verification could be verified using the configured policy.

Ordinarily, this method works as expected; however when working with self-signed certificates (or attempting to verify a certificate against a specific root CA), there are issues that require additional verifications by the developer that are not well documented by the .NET docs.

This repo contains code samples demonstrating how to properly validate certificates with .NET Core 3.x and 5+, including for self-signed certificate authorities (CAs).

Summary of issues

In .NET 5 and higher, a new X509ChainPolicy.TrustMode property is available which can override the OS trust stores and perform certificate verification using only roots and intermediaries added to the X509Chain.CustomTrustStore property, effectively explicitly pinning the root CA when performing verification; all is well.

In .NET Core 3.x and prior, the implementation has two 'gotchas' that are not well described in the X509-related class documentation:

  1. Certificates are always verified against the OS trust store, plus certificates added to ExtraStore.

    This means that a X509Chain.Build() verification only tells us only that a chain terminated in one of the trusted certificates, but does not permit us to specify which should have matched.

  2. When enabling the AllowUnknownCertificateAuthority flag to work with self-signed root CAs, both the UntrustedRoot and PartialChain statuses are ignored. Therefore, X509Chain.Build() will return true even if your certificate under validation was not issued by any of the trusted root CAs in the OS trusted roots or ExtraStore (i.e., it considers a new chain consisting only the certificate under validation and determines that to be a partial chain, which is then ignored). Up until very recently, this behavior was undocumented and the .NET docs incorrectly described behavior when enabling this flag.

Both of these gotchas require a developer perform manual verification of correct chain termination (i.e. checking the last item in the chain is indeed the signing root CA we expect), and needs to be done manually and separately from X509Chain.Build().

dotnet/runtime#26449 and dotnet/runtime#49615 have more details.

Thus, these code samples demonstrate both the older .NET Core-based method that includes an additional verification, as well as the newer .NET 5+ that supports verification against a specific root CA.

Code samples

The samples are inline C# code that makes use of dotnet script. If you do not have it, install with:

dotnet tool install -g dotnet-script

The scripts will load PEM-formatted files (provided the file extension is .pem), otherwise it assumes DER-formatted input files. Run them without arguments to view usage instructions.

I want to... Your target .NET SDK Code sample
Verify a certificate against CAs in OS trust store and/or ExtraStore .NET Core 1.x - 3.x or .NET 5+ certvalidate-anysdk.csx
Verify a certificate against a self-signed CA; or verify a certificate while pinning to a specific root CA .NET Core 1.x - 3.x certvalidate-selfsigned-dotnetcore.csx
Verify a certificate against a self-signed CA; or verify a certificate while pinning to a specific root CA .NET 5 or higher certvalidate-selfsigned-dotnet5+.csx

Note that all of the scripts make use of certvalidate-common.csx which includes some helper methods.

Generating sample data

Scripts to generate sample data are also included in the repo. Ensure you have OpenSSL installed and available on your $PATH to use them.

  1. Generate self-issued certificates: creates 2 self-signed root CAs and a single certificate from each (ca.foo.com issuing device01.foo.com and ca.bar.com issuing sensor01.bar.com), storing the certificates into the certificates folder:

    ./create_certificates.sh
  2. Well-known certificates: downloads the public X.509 certificates published by some well-known websites to the certificates folder:

    ./download_known_certificates.sh

Running the code samples

  1. Validate a well-known website's certificate against the OS trust store:

    dotnet-script certvalidate-anysdk.csx -- certificates/wikipedia.org.pem
  2. Validate a self-issued X.509 certificate against a self-signed root CA (via .NET Core 1.x-3.x APIs, and then .NET 5+ APIs):

    dotnet-script certvalidate-selfsigned-dotnetcore.csx -- certificates/device01.foo.com.pem certificates/ca.foo.com.pem
    
    dotnet-script certvalidate-selfsigned-dotnet5+.csx -- certificates/device01.foo.com.pem certificates/ca.foo.com.pem
  3. Now try it again, specifying the wrong root CA for the certificate under validation (we expect failures):

    dotnet-script certvalidate-selfsigned-dotnetcore.csx -- certificates/device01.foo.com.pem certificates/ca.bar.com.pem
    
    dotnet-script certvalidate-selfsigned-dotnet5+.csx -- certificates/device01.foo.com.pem certificates/ca.bar.com.pem

    Note how X509Chain.Build() returned true in the .NET Core samples, even though the certificate under verification was entirely unrelated to the CA! This is the PartialChain gotcha described above. Only after manual verification of the chain is the issue revealed.

  4. Try validating an otherwise well-known certificate but pin it against an unrelated root CA (again, we expect failures):

    dotnet-script certvalidate-selfsigned-dotnetcore.csx -- certificates/mozilla.org.pem certificates/ca.bar.com.pem
    
    dotnet-script certvalidate-selfsigned-dotnet5+.csx -- certificates/mozilla.org.pem certificates/ca.bar.com.pem

dotnet-x509-certificate-verification's People

Contributors

stewartadam avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

dotnet-x509-certificate-verification's Issues

Questions about validating the certificate that client receives from the server with intermediate CAs

Hello.

I was working in an application that use self signed certificates (CA, server and client) and i was thinking how to validate the certificate that the client receives from the server.

In my case, I distrubute the CA and the server certificates with the client application. Then I have to implement the method that validate the server certificate. In my case, the code is this:

private bool ValidarCertificadoServidor(object paramSender, X509Certificate? paramCertificadoServidor, X509Chain? paramCadenaCertificadosServidor, SslPolicyErrors paramErrores)
{
    paramCadenaCertificadosServidor!.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
    paramCadenaCertificadosServidor.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
    paramCadenaCertificadosServidor.ChainPolicy.CustomTrustStore.Add(_certificadoCA);


    X509Certificate2 miCertificaServidorConvertido = paramCadenaCertificadosServidor.ChainElements.ElementAt(0).Certificate;
    return paramCadenaCertificadosServidor.Build(miCertificaServidorConvertido);
}

In this case, I don't create a new chain, just I modificate the chain that I receive from the server, setting that I want to set the custom root trust.

In the custom trust store, I add the CA that the client has, it is the expected CA. So I don't use the CA that is include in the chain received from the server.

When I build it passing as parameter the first element of the chain received from the server, that is the certificate from the server, I get true. So it works. If I build without configurating it, I get false.

Well, my doubt is what is validated in the build. I guess that it validate that the server certificate is issued by my CA, but I could be wrong.

If I am right, it is an important validation, but I guess that still it is needed to compare the thumbprint of the server certificate that is distribute with the client application and the server certificate that is received from the server.

I think that this is needed because perhaps someone could stole the server certificate and the CA, after all, both are publics certificates. So comparing the thumbprints, I am ensuring that really the certificate of the server is the expected certificate.

I am right, that I should to compare the thumbprints of the certificates or is it not needed because it is validate in the build of the chain? But i guess it is not possible in the build because the chain that is received from the server doesn't know which is the server certificate that the client expects.

Thanks.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.