Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

X509Chain.Build() fails to return valid certificate chain on Linux (openssl) if CA store contains expired certificate for CA, even if store contains valid cross-signed certificate for the same CA #43884

Open
Tracked by #64488
egraff opened this issue Oct 27, 2020 · 3 comments

Comments

@egraff
Copy link

egraff commented Oct 27, 2020

Description

Found when trying to reproduce #43879 on Linux.

There used to be a bug in OpenSSL where X509_verify_cert() could fail to find a valid certificate chain if the CA store contained multiple certificates for the same CA subject name, and one of the certificates had expired. However, that has been fixed long ago. It seems, though, that (a variant of?) this problem might exist in .NET.

I've written a stand-alone test application that generates the following certificates:

    New root CA (CN=TEST_CERT_New_root_CA)
                   |
                   v
First intermediate (CN=TEST_CERT_Expired_root_now_valid_intermediate_CA)     Expired root CA (CN=TEST_CERT_Expired_root_now_valid_intermediate_CA)
                                                       |                                            |
                                                       v                                            v
                                                  Second intermediate (CN=TEST_CERT_Second_Intermediate_CA)
                                                                            |
                                                                            v
                                                          End entity (CN=some.server.invalid)

and installs the certificates in the CA store, and tries to build a certificate chain from the end-entity certificate. Ideally, I would have liked the scenario to be slightly different (letting an expiring intermediate be available as a new root CA certificate), but openssl would prefer the shorter certificate chain, so the right code paths would not be triggered to reproduce this issue.

Expand to see test code included below:
namespace OpenSslCertChainProblem
{
    using System;
    using System.Globalization;
    using System.IO;
    using System.Linq;
    using System.Security.Cryptography;
    using System.Security.Cryptography.X509Certificates;

    using CertificateRequest = System.Security.Cryptography.X509Certificates.CertificateRequest;

    internal class Program
    {
        private static readonly RSAParameters NewRootCaRsaParams = new RSAParameters
        {
            Modulus = new byte[]
            {
                0xbd, 0x25, 0xba, 0x8a, 0xe4, 0xf8, 0x3b, 0xd3, 0xd7, 0x73, 0xe4, 0x27, 0x48, 0xb8, 0xf9, 0xf0, 0x83,
                0x27, 0x95, 0x16, 0xec, 0xa5, 0xdf, 0xfa, 0x5d, 0xa0, 0x10, 0xe1, 0x6a, 0x9b, 0xa3, 0x60, 0xeb, 0xef,
                0x75, 0xfd, 0xbd, 0x4d, 0x59, 0x0a, 0xdb, 0x04, 0x11, 0x6b, 0x0f, 0xd4, 0xc3, 0xb5, 0xb4, 0x47, 0x7d,
                0xf9, 0x7a, 0x73, 0xd3, 0xf7, 0x3d, 0x57, 0xae, 0x77, 0x17, 0x7a, 0x48, 0x03, 0x27, 0x74, 0x59, 0xd6,
                0xea, 0x98, 0xea, 0xb8, 0xd6, 0x84, 0x32, 0xab, 0x8f, 0x48, 0x3a, 0x84, 0x88, 0xbd, 0x0a, 0xd0, 0xb9,
                0xd9, 0xc6, 0x0b, 0xce, 0x1c, 0x9f, 0x13, 0x43, 0xe1, 0x18, 0xfc, 0xf0, 0xad, 0xf1, 0x36, 0xd9, 0x28,
                0x5f, 0xca, 0x11, 0xda, 0x1a, 0x8d, 0x83, 0x4e, 0x0e, 0x03, 0x6e, 0x27, 0xc9, 0x17, 0x0f, 0x28, 0xf1,
                0xaa, 0x9c, 0xd9, 0xc2, 0xa0, 0xe4, 0xe4, 0xa1, 0x6a, 0xca, 0xdb, 0xd7, 0x79, 0x3c, 0x27, 0x00, 0x81,
                0xd2, 0x45, 0xe3, 0x17, 0x48, 0x32, 0x6b, 0x31, 0x43, 0xd8, 0x16, 0xfc, 0xd9, 0x47, 0xba, 0xd9, 0xe2,
                0xd6, 0xc6, 0x98, 0xdc, 0xae, 0xe4, 0xf0, 0xb6, 0x4d, 0xed, 0x11, 0xec, 0x40, 0x6a, 0x19, 0x10, 0x01,
                0x44, 0xf4, 0x8a, 0x27, 0xd9, 0x06, 0x04, 0xec, 0xe1, 0xcb, 0x12, 0x87, 0x0b, 0xc3, 0x39, 0x6b, 0x0c,
                0x95, 0x14, 0x87, 0xdb, 0x9b, 0xc2, 0x98, 0xd7, 0xf4, 0x4a, 0x60, 0xca, 0xd6, 0xed, 0xa0, 0xc0, 0x46,
                0xfe, 0x00, 0x69, 0xbd, 0xb4, 0x70, 0x55, 0x94, 0x49, 0x98, 0x03, 0x43, 0x68, 0xf0, 0x96, 0xd2, 0x61,
                0xd6, 0x39, 0xa1, 0x48, 0x03, 0x7e, 0x78, 0xc3, 0xdb, 0xeb, 0x36, 0xd1, 0x83, 0x31, 0x16, 0x62, 0x89,
                0x38, 0xb3, 0x35, 0xbf, 0x9d, 0x7c, 0xc7, 0x92, 0x4f, 0xff, 0xcc, 0x45, 0x41, 0xad, 0x02, 0x23, 0x4c,
                0x39,
            },
            Exponent = new byte[] { 0x01, 0x00, 0x01 },
            P = new byte[]
            {
                0xc9, 0x4c, 0x6e, 0x6c, 0x3a, 0x6e, 0x79, 0x1b, 0x2e, 0x1b, 0x31, 0x51, 0x3a, 0xe3, 0x92, 0xb2, 0xe2,
                0x81, 0x7c, 0x6c, 0xa2, 0xbc, 0xd1, 0xd7, 0x83, 0xbc, 0x4c, 0xa1, 0xbe, 0xc7, 0xb4, 0x6b, 0x2b, 0x2e,
                0x2e, 0x07, 0x39, 0xcd, 0x64, 0x9d, 0xe6, 0x0d, 0xc6, 0xa0, 0x1d, 0xc9, 0x40, 0xfd, 0xa0, 0xa1, 0xa0,
                0x35, 0x10, 0x7c, 0x60, 0xc0, 0x2d, 0xdd, 0x1c, 0x14, 0x8f, 0x3c, 0x8e, 0x86, 0x8b, 0x6d, 0x1a, 0xf8,
                0x74, 0xe2, 0xd8, 0x94, 0x4d, 0xd5, 0xda, 0x44, 0xbd, 0xf6, 0x08, 0xeb, 0xc3, 0x35, 0x02, 0xc4, 0x40,
                0x95, 0x37, 0x10, 0x0b, 0xc4, 0xa1, 0x1d, 0xb3, 0x4a, 0xe3, 0x3f, 0x30, 0x8f, 0x8c, 0x60, 0x9a, 0xe9,
                0x41, 0xb7, 0xe6, 0x56, 0x28, 0xa9, 0x49, 0x00, 0xd8, 0x95, 0x49, 0xec, 0x9a, 0xe1, 0x88, 0x88, 0xea,
                0x6e, 0x64, 0x1b, 0xbf, 0xee, 0x5d, 0x1a, 0x3c, 0xd7,
            },
            Q = new byte[]
            {
                0xf0, 0x8b, 0xfc, 0x77, 0xd8, 0x73, 0x32, 0xd6, 0xba, 0x57, 0x97, 0xa2, 0xf9, 0xbc, 0xb5, 0x46, 0xee,
                0x8d, 0x54, 0xa1, 0xe0, 0x76, 0xa7, 0x4c, 0x22, 0x93, 0x57, 0x91, 0xa5, 0x2f, 0x14, 0xb8, 0x27, 0x78,
                0x71, 0xe7, 0xf4, 0xca, 0x49, 0xce, 0xc6, 0xa7, 0x56, 0x0f, 0x7b, 0xd1, 0xe9, 0xc7, 0x68, 0x97, 0x48,
                0x1e, 0x3e, 0xaa, 0x15, 0x9a, 0x6c, 0xc0, 0xcb, 0x73, 0xdb, 0x89, 0x27, 0xaf, 0xa2, 0x2e, 0x35, 0x5a,
                0x00, 0x4e, 0x4a, 0xd6, 0x60, 0x79, 0xb2, 0x37, 0xb7, 0x76, 0x0b, 0xeb, 0x69, 0x17, 0x87, 0x0f, 0x76,
                0xe4, 0xf4, 0xcb, 0xe4, 0xd7, 0xda, 0x90, 0xbc, 0x68, 0xe0, 0xcc, 0x71, 0x29, 0xee, 0x9e, 0x6d, 0xe7,
                0x8b, 0xdd, 0x25, 0x55, 0x1c, 0x3a, 0x69, 0x71, 0xfa, 0xd9, 0xec, 0xa5, 0xf3, 0x4b, 0x4e, 0x5c, 0x80,
                0xc0, 0xa8, 0x73, 0x7a, 0x52, 0x45, 0x34, 0x0d, 0x6f,
            },
            D = new byte[]
            {
                0x19, 0xc5, 0x27, 0x2a, 0x3f, 0x51, 0xf6, 0xcc, 0xc1, 0x01, 0x00, 0x27, 0xbe, 0x07, 0xbd, 0xbd, 0xe0,
                0x10, 0xa7, 0x86, 0x6d, 0x0d, 0x98, 0x7b, 0x83, 0x00, 0x08, 0xec, 0xbd, 0x5a, 0xa1, 0x22, 0xd6, 0x62,
                0x54, 0xc0, 0x12, 0x67, 0x94, 0x67, 0x1f, 0x39, 0xe9, 0xa9, 0x9d, 0x89, 0x8e, 0x74, 0x96, 0x30, 0x5b,
                0x60, 0x7f, 0x27, 0x82, 0xcc, 0x9f, 0xa5, 0xef, 0x96, 0x38, 0x33, 0xa7, 0xd6, 0x0e, 0x2d, 0x13, 0x04,
                0x6e, 0x08, 0x11, 0x8a, 0xc7, 0x67, 0x51, 0x84, 0x00, 0x4c, 0x85, 0xf4, 0x82, 0xb0, 0xc6, 0xe5, 0x73,
                0x3c, 0xd1, 0x77, 0xd0, 0x48, 0x24, 0x00, 0x3a, 0xf2, 0x53, 0x36, 0x3b, 0x10, 0x67, 0xd9, 0xe2, 0x32,
                0x57, 0x02, 0xaf, 0xf5, 0xf8, 0x42, 0x29, 0x8e, 0x9b, 0xe8, 0x29, 0xbc, 0xef, 0xef, 0xdd, 0x57, 0xe5,
                0xbd, 0x2a, 0x0b, 0x9d, 0x31, 0xd0, 0x9d, 0xb9, 0xc4, 0x17, 0xd6, 0x50, 0x97, 0xed, 0xfc, 0x8d, 0x2d,
                0xe2, 0x5c, 0xa9, 0xa5, 0xf7, 0xc7, 0xa4, 0x56, 0xac, 0xa3, 0xf1, 0x8c, 0x2c, 0x5c, 0x2c, 0x2b, 0xc9,
                0x09, 0x5c, 0x4d, 0x58, 0xea, 0x34, 0x97, 0x82, 0x29, 0x4c, 0x20, 0x1c, 0x98, 0xfd, 0xa0, 0xda, 0x12,
                0x89, 0x36, 0x03, 0x54, 0x17, 0x75, 0xb1, 0x15, 0x6f, 0x3a, 0xba, 0xfa, 0xd0, 0xc0, 0x65, 0x4a, 0x6b,
                0xee, 0xd2, 0xbe, 0x29, 0xdc, 0x32, 0x35, 0x6c, 0xa2, 0x46, 0xea, 0x6c, 0x34, 0x1a, 0x58, 0x54, 0xe7,
                0x16, 0x3e, 0xa7, 0x66, 0x95, 0x4b, 0xd3, 0x39, 0xd4, 0x6e, 0x8e, 0x67, 0x81, 0x84, 0x6c, 0x02, 0x34,
                0xbe, 0x2f, 0x29, 0x09, 0x73, 0xf2, 0x0f, 0x46, 0xc5, 0x97, 0xee, 0xef, 0x86, 0xa0, 0x9e, 0xb3, 0x51,
                0x09, 0xd0, 0xfe, 0xd6, 0x43, 0xcc, 0xcf, 0x6c, 0xae, 0xec, 0x26, 0x02, 0x85, 0xf6, 0x65, 0x04, 0x1f,
                0x75,
            },
            DP = new byte[]
            {
                0x27, 0xee, 0xd2, 0xa5, 0x03, 0xbe, 0x70, 0xa5, 0xf4, 0xb2, 0x8e, 0x49, 0x07, 0xfe, 0xa7, 0x56, 0x77,
                0x5a, 0xaa, 0x9d, 0x3e, 0x77, 0x68, 0xc5, 0x71, 0x2b, 0xc4, 0xa3, 0xfd, 0x9d, 0xce, 0x6b, 0xc8, 0x28,
                0x24, 0x17, 0x68, 0xdf, 0x17, 0x0b, 0xbf, 0xdc, 0x91, 0xbf, 0xa7, 0xc9, 0x09, 0xec, 0x86, 0x96, 0x29,
                0x9a, 0xfd, 0x47, 0x3f, 0x46, 0x40, 0x92, 0xf1, 0x84, 0x1e, 0x9a, 0x2b, 0xc8, 0x03, 0x16, 0xc4, 0xd7,
                0xef, 0x10, 0x25, 0x58, 0xde, 0x4c, 0x03, 0x3d, 0xc9, 0xd5, 0x6a, 0x15, 0xa3, 0x26, 0xfc, 0x02, 0x84,
                0xb1, 0x55, 0x38, 0x08, 0xad, 0x9c, 0x2c, 0xb1, 0x29, 0xb5, 0x05, 0x0f, 0x14, 0x1b, 0x0b, 0xfb, 0x0f,
                0x34, 0xeb, 0x4f, 0x75, 0x5e, 0x21, 0x9f, 0x0a, 0xdc, 0xec, 0x58, 0xba, 0xe8, 0x4a, 0xbf, 0x74, 0x31,
                0xd5, 0x6e, 0x64, 0x96, 0xe1, 0xa9, 0x74, 0x90, 0x07,
            },
            DQ = new byte[]
            {
                0x94, 0x29, 0xb5, 0x5d, 0x60, 0x4e, 0x3c, 0x9f, 0x34, 0x71, 0xe5, 0x6d, 0xb6, 0x23, 0xbd, 0x08, 0xae,
                0xc1, 0x10, 0xc3, 0x10, 0x8e, 0x25, 0x27, 0xe9, 0x18, 0x16, 0x55, 0x18, 0x88, 0xb2, 0x5b, 0x67, 0xbc,
                0x71, 0x74, 0xad, 0x3b, 0xb8, 0x50, 0x8b, 0xdd, 0xe0, 0x4d, 0x73, 0x37, 0xed, 0xa6, 0x2c, 0xcf, 0x19,
                0xd5, 0xbf, 0x45, 0x66, 0x3f, 0x13, 0x03, 0x81, 0x8d, 0xa0, 0x48, 0x8f, 0xd0, 0x47, 0xb5, 0xec, 0x98,
                0xbe, 0x1d, 0x7b, 0x4d, 0x26, 0x69, 0x6f, 0x13, 0x90, 0x86, 0x3e, 0x7b, 0x46, 0xe4, 0xfe, 0x04, 0xd1,
                0x77, 0xdf, 0x2c, 0x83, 0x8d, 0x4d, 0x0b, 0xdf, 0x71, 0x3e, 0x1d, 0xcf, 0x0a, 0x43, 0xf7, 0x03, 0xbd,
                0x1e, 0x52, 0x1d, 0xbf, 0x99, 0xbe, 0x84, 0xda, 0x76, 0xe7, 0xaa, 0x32, 0xe1, 0x73, 0x9e, 0x15, 0xae,
                0x1c, 0x7c, 0xe5, 0x7c, 0x0f, 0x96, 0x9e, 0xa4, 0x33,
            },
            InverseQ = new byte[]
            {
                0x60, 0x63, 0x29, 0x5e, 0x8a, 0x80, 0x9b, 0x52, 0xdc, 0x0a, 0x9e, 0x81, 0x99, 0x53, 0x40, 0x54, 0x74,
                0x6b, 0x11, 0xfe, 0xc8, 0x8f, 0x7b, 0x4b, 0x67, 0x9d, 0x2a, 0x83, 0x53, 0x32, 0x32, 0xec, 0xa4, 0xa7,
                0xaf, 0xeb, 0x4d, 0xac, 0xa4, 0x4b, 0xe0, 0xc3, 0x00, 0x01, 0x5d, 0x00, 0xef, 0x88, 0xea, 0x07, 0xa7,
                0xe5, 0xc1, 0x29, 0xde, 0xdf, 0x9b, 0x58, 0xed, 0xd0, 0x2a, 0xa3, 0xd9, 0x3a, 0x1c, 0xc5, 0xb4, 0x06,
                0xe8, 0xe8, 0x50, 0x03, 0x3d, 0x8b, 0xec, 0xa6, 0x1a, 0x8c, 0xe2, 0xab, 0x36, 0x54, 0x4c, 0xcd, 0x37,
                0x41, 0x8d, 0xd3, 0x6f, 0x3a, 0xc9, 0x97, 0xd3, 0x39, 0xdc, 0xf4, 0x19, 0x4a, 0xa7, 0xf1, 0x99, 0x1d,
                0x4e, 0x72, 0xb8, 0xd3, 0x3c, 0x3e, 0x8d, 0xf9, 0x3e, 0xfd, 0xc2, 0xb1, 0xc6, 0xb1, 0x75, 0x22, 0x72,
                0x04, 0x7e, 0x84, 0xc4, 0x1b, 0xf0, 0xa0, 0xaa, 0x75,
            },
        };

        private static readonly RSAParameters FirstIntermediateRsaParams = new RSAParameters
        {
            Modulus = new byte[]
            {
                0xa1, 0xed, 0x77, 0x2d, 0x3d, 0xd6, 0x6d, 0xd3, 0x5c, 0x7c, 0xe9, 0x96, 0x73, 0x51, 0x8b, 0x1f, 0xc7,
                0x9a, 0xca, 0xde, 0x28, 0x5b, 0xec, 0x05, 0xe1, 0x45, 0xac, 0xa0, 0x55, 0x78, 0x36, 0xb9, 0xd6, 0xb7,
                0x10, 0x67, 0xc2, 0x8d, 0xd6, 0xa1, 0x4a, 0x5f, 0x9e, 0xeb, 0xc1, 0x21, 0x7e, 0x5a, 0x6b, 0xd7, 0x4d,
                0x73, 0xbf, 0x72, 0x6f, 0x26, 0x32, 0x41, 0x44, 0x90, 0x4e, 0x00, 0x57, 0x2b, 0xd8, 0xf8, 0x44, 0x1a,
                0xc4, 0x58, 0x2e, 0x8c, 0x7b, 0x7c, 0x05, 0x5f, 0x2e, 0x53, 0xb4, 0xc7, 0x3f, 0x08, 0x8d, 0x38, 0xe8,
                0x12, 0x72, 0x29, 0xa0, 0x3c, 0x89, 0x4b, 0xd5, 0x2c, 0xe5, 0x52, 0xe0, 0xc8, 0x29, 0xf4, 0x53, 0x19,
                0x33, 0xb4, 0x5a, 0xb7, 0x0d, 0xdd, 0xd7, 0x51, 0x0d, 0xa8, 0x12, 0x6d, 0x5f, 0x63, 0x5d, 0x50, 0x68,
                0x45, 0x70, 0xd8, 0xe3, 0x63, 0x05, 0x05, 0x16, 0x33, 0x6a, 0xf3, 0x16, 0xd2, 0xfe, 0x10, 0x28, 0x11,
                0x5d, 0xee, 0xa2, 0x06, 0xa3, 0x09, 0x5f, 0xa9, 0x41, 0xa7, 0xae, 0xa6, 0x74, 0xa0, 0x52, 0xa2, 0x5a,
                0x49, 0xf9, 0x05, 0x74, 0x21, 0xe4, 0x9f, 0xd3, 0x5a, 0xea, 0xa2, 0xf8, 0x6f, 0x54, 0xf0, 0xc3, 0x8b,
                0x87, 0x91, 0x79, 0x4c, 0x99, 0x3f, 0x76, 0xe2, 0x2f, 0x34, 0x8c, 0xab, 0xef, 0x33, 0x4f, 0x7c, 0x57,
                0xef, 0x3d, 0xe2, 0x9f, 0x3d, 0xbc, 0x79, 0x7f, 0xd1, 0x9d, 0xca, 0x99, 0x3c, 0xda, 0x10, 0xab, 0x59,
                0xd0, 0x86, 0x49, 0x04, 0x09, 0x27, 0x21, 0xc6, 0xa9, 0x77, 0xc4, 0xef, 0x0c, 0xab, 0xf6, 0x45, 0x76,
                0x1e, 0xba, 0xaa, 0x7c, 0xee, 0x6c, 0xa3, 0xe4, 0x48, 0x2f, 0xa0, 0x31, 0x4f, 0x5b, 0xee, 0xa5, 0xa6,
                0xec, 0x00, 0xc4, 0x8e, 0xe0, 0x2e, 0xd9, 0x74, 0xa8, 0x4c, 0x60, 0xe1, 0x71, 0xca, 0x2c, 0x9d, 0xef,
                0xb1,
            },
            Exponent = new byte[] { 0x01, 0x00, 0x01 },
            P = new byte[]
            {
                0xcf, 0x75, 0xda, 0x43, 0x7f, 0x9c, 0x16, 0xfe, 0x17, 0xf0, 0xba, 0x7f, 0x59, 0x6a, 0xa1, 0xa7, 0xd0,
                0xed, 0xf8, 0xdf, 0x0d, 0x2e, 0xa8, 0xf9, 0x5d, 0xec, 0xd1, 0x2b, 0x66, 0x59, 0x70, 0xbb, 0x6e, 0x85,
                0xfe, 0x90, 0x0c, 0x9f, 0x56, 0xac, 0x38, 0x7b, 0x0e, 0x87, 0x25, 0xd4, 0xff, 0xe2, 0xb6, 0xc0, 0xfd,
                0xdf, 0xc9, 0x2b, 0x7c, 0x4b, 0x2f, 0xfc, 0x84, 0xc8, 0x87, 0x45, 0x09, 0x19, 0xf1, 0x32, 0x1c, 0x5d,
                0xf5, 0x9b, 0x6a, 0x75, 0x95, 0x6a, 0x4e, 0x6f, 0x6a, 0xfe, 0xe3, 0xad, 0x8f, 0x0e, 0x32, 0x9c, 0xd7,
                0x82, 0x5c, 0x01, 0x42, 0x0f, 0x7a, 0xee, 0x32, 0x44, 0x8a, 0x83, 0x5e, 0x30, 0x7f, 0xc2, 0xc2, 0x1d,
                0x3e, 0xed, 0x0d, 0x7a, 0x65, 0x10, 0x92, 0x32, 0x2e, 0x9b, 0x23, 0xeb, 0x71, 0xa5, 0x9e, 0xb4, 0xe1,
                0x0b, 0xb3, 0x09, 0xfb, 0x8d, 0x0a, 0xee, 0xf5, 0x13,
            },
            Q = new byte[]
            {
                0xc7, 0xd0, 0x5c, 0x3f, 0x07, 0xd8, 0x36, 0x9b, 0x3b, 0x1f, 0xeb, 0xef, 0x29, 0x96, 0x8d, 0xbf, 0x79,
                0x42, 0xea, 0x06, 0xc8, 0x0c, 0x18, 0x40, 0xca, 0xc8, 0x86, 0x6d, 0x65, 0xb4, 0x65, 0x5f, 0xe5, 0x44,
                0xaf, 0x93, 0xfd, 0x41, 0x56, 0xae, 0x7e, 0x08, 0x84, 0x5a, 0xab, 0x84, 0x23, 0x63, 0x84, 0x80, 0x52,
                0x32, 0x48, 0x4c, 0x17, 0x8a, 0x9a, 0xe8, 0xd6, 0xb6, 0x25, 0xb9, 0xe6, 0x11, 0x56, 0xa4, 0x12, 0x6b,
                0x46, 0x57, 0x10, 0xb2, 0xef, 0xe6, 0x75, 0x00, 0x5b, 0xe8, 0x7d, 0x50, 0x2e, 0xc4, 0xd0, 0xba, 0x8b,
                0x26, 0x32, 0xc1, 0x0c, 0x3a, 0xdc, 0xfe, 0xc7, 0xe0, 0x37, 0xc7, 0x68, 0xbf, 0x2a, 0x4a, 0x0a, 0x35,
                0x0b, 0x60, 0xe4, 0xfe, 0x7d, 0xc0, 0xe9, 0xbc, 0x69, 0x3b, 0x3a, 0x92, 0x41, 0x72, 0x33, 0xba, 0x04,
                0x62, 0x20, 0xcf, 0x23, 0x8c, 0x7f, 0xd2, 0x54, 0xab,
            },
            D = new byte[]
            {
                0x0e, 0xf7, 0x2c, 0x81, 0x90, 0xea, 0x85, 0x81, 0xf8, 0x55, 0x4f, 0x1d, 0x69, 0x91, 0x0a, 0xc3, 0xa8,
                0x26, 0x05, 0x43, 0xdd, 0x05, 0x09, 0xde, 0x4d, 0x40, 0xac, 0x40, 0xb1, 0x45, 0x88, 0x01, 0x4a, 0xf7,
                0xe6, 0x5b, 0x40, 0x9e, 0xed, 0x4c, 0x40, 0xdc, 0x6f, 0x8a, 0xa8, 0x71, 0xd9, 0x04, 0xb0, 0x5c, 0xd8,
                0xba, 0xae, 0xe1, 0xfb, 0xa5, 0xd3, 0x15, 0x37, 0x2a, 0x03, 0x1b, 0x82, 0xf2, 0xf9, 0x7d, 0x90, 0x85,
                0xcc, 0xc9, 0xbd, 0x9b, 0x9c, 0x3a, 0x0d, 0x21, 0x0b, 0xde, 0x0e, 0xa2, 0x2f, 0x90, 0x31, 0xc5, 0x25,
                0xa5, 0xb6, 0xc9, 0x64, 0x16, 0xd4, 0x35, 0x9f, 0x01, 0x24, 0x3c, 0xb1, 0xab, 0xae, 0x3c, 0xf7, 0x85,
                0x27, 0xc3, 0x2e, 0x73, 0x26, 0xa3, 0xef, 0x27, 0x7d, 0x94, 0xa0, 0x07, 0x5d, 0xbe, 0x88, 0x3c, 0x4b,
                0x36, 0xc7, 0xc4, 0x33, 0xd7, 0xcd, 0xf9, 0xf7, 0x60, 0x29, 0x89, 0x1b, 0xbe, 0x34, 0x08, 0x19, 0x7b,
                0xe8, 0xbd, 0x35, 0x68, 0x8c, 0xe5, 0x54, 0xd8, 0x73, 0xef, 0x95, 0xcb, 0x3a, 0xb0, 0x1f, 0x6d, 0x3a,
                0xcf, 0xda, 0xcf, 0x79, 0x05, 0x49, 0x86, 0x33, 0x67, 0xf8, 0x3f, 0x5e, 0xfa, 0xee, 0x40, 0x2e, 0xfa,
                0x22, 0x42, 0x2d, 0xca, 0x91, 0x87, 0xb4, 0x5c, 0x1c, 0xe3, 0x49, 0xf1, 0xbf, 0xd3, 0xf7, 0xd2, 0x20,
                0x88, 0xa2, 0xcb, 0x78, 0x55, 0x6c, 0x54, 0x3f, 0xc3, 0xff, 0x8f, 0x02, 0xd0, 0xac, 0x38, 0x84, 0x4d,
                0x9c, 0x10, 0x04, 0xaf, 0xb8, 0x3c, 0x91, 0x07, 0x85, 0xca, 0xd6, 0xdd, 0xf1, 0xc6, 0x81, 0xe4, 0xf9,
                0xa8, 0x53, 0xad, 0x3c, 0xdb, 0xcb, 0xce, 0x3d, 0x17, 0xab, 0xd7, 0x1b, 0x19, 0xa5, 0x5d, 0xed, 0xa2,
                0x2c, 0x9d, 0x32, 0x8b, 0xb9, 0xd8, 0x1b, 0xfd, 0x0b, 0x52, 0x98, 0x44, 0x41, 0xa8, 0x33, 0x7d, 0x7a,
                0x15,
            },
            DP = new byte[]
            {
                0x40, 0x95, 0x5d, 0xb7, 0x39, 0x98, 0xac, 0x07, 0xba, 0x08, 0x34, 0xe6, 0xc4, 0x3d, 0x4f, 0xc9, 0xe0,
                0x5e, 0xd4, 0xe5, 0x0b, 0x43, 0x85, 0x52, 0xec, 0x0c, 0x77, 0x29, 0x5d, 0x99, 0x66, 0x60, 0xd3, 0x0d,
                0x5f, 0xac, 0x14, 0x61, 0xde, 0x27, 0x8b, 0x05, 0xb4, 0x06, 0x1a, 0x9e, 0xee, 0x71, 0xf3, 0x96, 0xac,
                0xf1, 0xe7, 0xbc, 0x63, 0xdb, 0x3c, 0x3b, 0x0e, 0x53, 0x26, 0xd9, 0x34, 0xf6, 0x20, 0x82, 0x0b, 0x16,
                0xad, 0x09, 0x0e, 0xe1, 0x3f, 0xfc, 0x72, 0x22, 0x79, 0x5a, 0x94, 0x22, 0x49, 0xb0, 0x8f, 0xcd, 0x07,
                0xc2, 0x0b, 0x46, 0x50, 0xfc, 0x64, 0xb6, 0x96, 0x6b, 0x83, 0xfb, 0x55, 0x6d, 0x1f, 0xb8, 0xcf, 0x99,
                0x2f, 0x27, 0xb8, 0xd4, 0x6b, 0x75, 0xf7, 0x2d, 0x2c, 0x19, 0x1b, 0xdb, 0x85, 0xeb, 0x9b, 0x36, 0x90,
                0x00, 0x86, 0x62, 0x0b, 0x2f, 0x63, 0x41, 0xf4, 0x85,
            },
            DQ = new byte[]
            {
                0x48, 0xf4, 0x12, 0x3a, 0x11, 0x2b, 0x28, 0x04, 0xd5, 0x39, 0x7a, 0x72, 0xff, 0xc4, 0x30, 0x4c, 0xd0,
                0x81, 0x55, 0xe0, 0xd3, 0xf9, 0x56, 0x81, 0x91, 0x88, 0x55, 0x4c, 0x6b, 0xed, 0xc9, 0x75, 0x91, 0xc7,
                0xda, 0x33, 0x36, 0x2c, 0x3c, 0xf1, 0xc8, 0xa2, 0x63, 0x70, 0x97, 0x5e, 0xcf, 0x6c, 0xee, 0x5b, 0xcb,
                0xa7, 0x00, 0x2e, 0x88, 0x09, 0xdb, 0x35, 0x57, 0xef, 0x79, 0x26, 0xbe, 0x91, 0x66, 0x08, 0xd1, 0x90,
                0xc9, 0x45, 0xd0, 0x9b, 0x04, 0x6c, 0x28, 0x1b, 0xd1, 0x72, 0xb9, 0x0e, 0x25, 0x85, 0x46, 0x90, 0x55,
                0x6d, 0xc4, 0xea, 0x27, 0x04, 0xf6, 0xdb, 0x2f, 0x2e, 0xf6, 0x2e, 0xa0, 0x57, 0xeb, 0xa0, 0xcc, 0xc9,
                0x41, 0x36, 0x47, 0x65, 0x83, 0x94, 0x83, 0xc2, 0x7e, 0xc9, 0x59, 0x41, 0x3d, 0x13, 0xed, 0x3e, 0xe5,
                0x8a, 0xce, 0x5f, 0xd2, 0xa2, 0xe1, 0xa8, 0x71, 0xb1,
            },
            InverseQ = new byte[]
            {
                0x65, 0xdf, 0x97, 0xf3, 0x01, 0x6b, 0x10, 0x0a, 0xac, 0x80, 0x08, 0xac, 0xa5, 0x17, 0xd5, 0xeb, 0x2f,
                0x9a, 0xe4, 0xca, 0xa2, 0x19, 0x39, 0xd0, 0xe9, 0x15, 0xb0, 0x7e, 0xbd, 0xac, 0x7f, 0x04, 0x50, 0x34,
                0x01, 0xc2, 0x1d, 0xb3, 0x55, 0x3a, 0xc2, 0x76, 0x59, 0x74, 0x6b, 0xf0, 0x8e, 0x1a, 0x9c, 0x5d, 0xb6,
                0x89, 0x45, 0x08, 0xfb, 0x93, 0x34, 0x47, 0x00, 0x39, 0x92, 0xc2, 0xc3, 0xdb, 0x09, 0x23, 0x67, 0x7a,
                0xd2, 0x49, 0x6f, 0x09, 0xbc, 0xb7, 0x54, 0x2b, 0x35, 0xfe, 0xfa, 0x44, 0x48, 0x53, 0x1f, 0x78, 0x90,
                0xca, 0x4a, 0x1a, 0xa0, 0xe2, 0x60, 0xca, 0xfc, 0x38, 0xec, 0x23, 0xfb, 0xda, 0x4e, 0x6d, 0xac, 0xf8,
                0x57, 0x73, 0xc3, 0x58, 0xb9, 0x3f, 0xed, 0x5e, 0x13, 0xd4, 0xed, 0x97, 0x40, 0xd9, 0xfa, 0x88, 0xde,
                0x13, 0x48, 0xc0, 0x38, 0xd6, 0xad, 0x7c, 0x58, 0xb9,
            },
        };

        private static readonly RSAParameters SecondIntermediateRsaParams = new RSAParameters
        {
            Modulus = new byte[]
            {
                0xcf, 0xfc, 0xb1, 0x72, 0xcb, 0xeb, 0xc4, 0x67, 0xff, 0xc9, 0xc3, 0x48, 0xdc, 0x69, 0xd9, 0x78, 0xa6,
                0xc1, 0x66, 0x88, 0x7e, 0x69, 0xba, 0x6c, 0xd4, 0xf2, 0x9d, 0x4c, 0xf9, 0x1a, 0xdc, 0xc3, 0xd3, 0x45,
                0x89, 0xd1, 0xcf, 0x68, 0x40, 0x99, 0x9a, 0x32, 0xa9, 0xfd, 0x70, 0x8a, 0xa2, 0xeb, 0xeb, 0xda, 0x0d,
                0x1a, 0x8e, 0xb8, 0x2e, 0x5a, 0xe8, 0xa9, 0x84, 0x1f, 0xc2, 0x8b, 0x32, 0x92, 0x91, 0x3e, 0xec, 0x07,
                0x93, 0xb6, 0x43, 0x2d, 0xf6, 0xe6, 0x6a, 0x65, 0x10, 0xd9, 0x43, 0xbb, 0x64, 0xb7, 0xe5, 0xd6, 0xfb,
                0x60, 0xfc, 0x2f, 0x63, 0xe4, 0xcb, 0xcc, 0xe1, 0x75, 0x0f, 0x0f, 0x08, 0x6d, 0x02, 0x33, 0x15, 0xbe,
                0x49, 0x49, 0xb2, 0xbe, 0x72, 0x0c, 0x31, 0xf3, 0x8c, 0x3f, 0xc0, 0xa4, 0x2b, 0xbe, 0x35, 0xd7, 0x4b,
                0xa0, 0x3b, 0xca, 0xbf, 0x12, 0xa4, 0x7b, 0x98, 0xa9, 0x74, 0x51, 0xca, 0xb7, 0x58, 0xa8, 0x7d, 0x12,
                0x1c, 0x8d, 0xc7, 0x25, 0xa2, 0xe2, 0x1e, 0xf6, 0x47, 0x85, 0xf2, 0x42, 0xc7, 0x20, 0x1d, 0xcd, 0xf2,
                0xca, 0x3a, 0x2e, 0xe5, 0x18, 0xeb, 0xe5, 0xbb, 0xc6, 0x34, 0x76, 0x47, 0x33, 0x12, 0xe4, 0xd5, 0x05,
                0xcf, 0x63, 0x42, 0xb6, 0x54, 0x7d, 0x6f, 0x3b, 0x47, 0x8d, 0x82, 0xe8, 0x08, 0x66, 0x33, 0xc0, 0xc3,
                0x2c, 0x23, 0xe7, 0x75, 0x88, 0x33, 0x49, 0xfd, 0x4d, 0xea, 0x86, 0xf2, 0x47, 0xab, 0x5b, 0x83, 0x47,
                0x4e, 0x42, 0x8c, 0xfe, 0xf2, 0xff, 0x14, 0x2b, 0xb8, 0x33, 0x61, 0xf9, 0x13, 0x1b, 0x72, 0xc9, 0xac,
                0x91, 0x94, 0xe1, 0xc9, 0xa0, 0x3a, 0x65, 0x9d, 0x2d, 0x2c, 0xf6, 0x6f, 0x8f, 0xae, 0x96, 0x6a, 0x76,
                0xfb, 0xa0, 0x1d, 0x61, 0xef, 0x92, 0x4c, 0x8a, 0x1f, 0x7f, 0xb4, 0x75, 0x8c, 0xdf, 0x06, 0x0b, 0x20,
                0x01,
            },
            Exponent = new byte[] { 0x01, 0x00, 0x01 },
            P = new byte[]
            {
                0xdc, 0x0e, 0x30, 0x3b, 0x53, 0x44, 0xc9, 0xf1, 0x4f, 0x56, 0x33, 0x3e, 0x8c, 0xa6, 0x2f, 0xfa, 0x8c,
                0x6a, 0xa9, 0x9a, 0x84, 0x6e, 0x7d, 0xf3, 0x29, 0x3e, 0xaf, 0x59, 0x70, 0x44, 0x8c, 0x8c, 0x74, 0x78,
                0x3c, 0xb0, 0x45, 0x9c, 0x4d, 0x1c, 0xc0, 0x18, 0x82, 0x04, 0x97, 0xe6, 0x01, 0x1c, 0xa2, 0xf8, 0x06,
                0xbe, 0x14, 0x5f, 0xe9, 0xcf, 0x30, 0xa7, 0x94, 0xe5, 0x45, 0xf2, 0xc5, 0x82, 0x10, 0x61, 0xb7, 0x4d,
                0x9d, 0xb8, 0xc8, 0x42, 0x49, 0x32, 0x48, 0xc3, 0x74, 0x16, 0xf7, 0x4e, 0x7c, 0x38, 0x50, 0xf0, 0x30,
                0x95, 0x9d, 0x8d, 0x93, 0xc1, 0xee, 0xa1, 0x61, 0xa3, 0xb0, 0xc6, 0x80, 0x26, 0x3b, 0x71, 0x8d, 0x25,
                0x72, 0xba, 0xf5, 0xab, 0x70, 0xf0, 0x64, 0x9c, 0xf2, 0x72, 0x7b, 0x17, 0x03, 0xf2, 0xee, 0x97, 0xd8,
                0x09, 0x83, 0x21, 0x58, 0xd9, 0x24, 0xfb, 0x15, 0x17,
            },
            Q = new byte[]
            {
                0xf1, 0xf5, 0xdb, 0x3f, 0x93, 0xbf, 0x6d, 0xc2, 0x6e, 0xa5, 0x38, 0xb0, 0x16, 0x2c, 0xed, 0xdc, 0x64,
                0x06, 0xa0, 0x74, 0x06, 0x6a, 0xf6, 0xdc, 0xa5, 0x49, 0xd6, 0x6a, 0xdd, 0x83, 0x7f, 0x68, 0x48, 0xa9,
                0x60, 0xd2, 0xaf, 0x8e, 0x0c, 0xa3, 0xb0, 0x10, 0x31, 0x1f, 0x10, 0xdc, 0x6c, 0x02, 0xaa, 0x7e, 0x5c,
                0x62, 0x0d, 0x57, 0x7b, 0x73, 0x15, 0xa7, 0x9c, 0xda, 0xca, 0xcf, 0x65, 0xfb, 0xbe, 0x8c, 0xbf, 0x23,
                0x25, 0x79, 0x00, 0x4b, 0x2e, 0x19, 0xad, 0xed, 0x0c, 0x9b, 0x58, 0x89, 0xa5, 0x1e, 0xc0, 0x6b, 0x7a,
                0x0d, 0xdb, 0x13, 0x56, 0x57, 0x4a, 0xf3, 0xed, 0x79, 0x2f, 0xd0, 0x35, 0xbd, 0x69, 0xe7, 0xfb, 0xbb,
                0x66, 0x4e, 0x80, 0x15, 0xd4, 0xb8, 0x84, 0xa4, 0xd2, 0x2a, 0x39, 0xc5, 0x1c, 0x64, 0x25, 0x8f, 0x49,
                0x51, 0xc5, 0x26, 0xe1, 0x8b, 0xcf, 0xd9, 0x52, 0xa7,
            },
            D = new byte[]
            {
                0x71, 0x54, 0x21, 0x64, 0x89, 0xd4, 0xd7, 0xda, 0x4b, 0x38, 0x27, 0x1e, 0x86, 0x6b, 0x6e, 0xc2, 0x1d,
                0x2a, 0xf8, 0xb1, 0x9b, 0x20, 0x05, 0x22, 0xd4, 0x0d, 0xb6, 0xc4, 0x80, 0x5f, 0xb0, 0xb7, 0x4b, 0x61,
                0x72, 0xad, 0x4e, 0x34, 0x62, 0x2e, 0xd0, 0x24, 0x97, 0x17, 0xaf, 0xc0, 0xfa, 0xf1, 0x4e, 0x3c, 0x2c,
                0x33, 0xb2, 0x5c, 0x8d, 0x75, 0x9f, 0x9c, 0xee, 0x8f, 0x57, 0xb3, 0xfd, 0x44, 0x7b, 0xbc, 0x2b, 0x64,
                0x1d, 0x8a, 0x8c, 0xa4, 0x79, 0x0a, 0x28, 0x23, 0x15, 0xc1, 0x2c, 0xf4, 0xe8, 0xf6, 0x83, 0x58, 0x56,
                0xf5, 0x0b, 0xb4, 0xce, 0xdb, 0x96, 0x68, 0xf6, 0xd8, 0x8f, 0xf2, 0x03, 0x07, 0x52, 0xd1, 0x7f, 0x21,
                0x86, 0x51, 0x90, 0x01, 0xd1, 0xa6, 0xef, 0x78, 0xe3, 0x8c, 0xc1, 0x6d, 0x33, 0x20, 0x9b, 0xb9, 0xca,
                0x4d, 0x6f, 0x07, 0x14, 0x82, 0x15, 0xf9, 0xfa, 0xbe, 0x2f, 0x11, 0xe2, 0x08, 0x0f, 0x6c, 0x11, 0x18,
                0x50, 0xb3, 0xb2, 0xdc, 0x0f, 0x3e, 0x1d, 0xc0, 0x73, 0xf4, 0x9f, 0x8a, 0x86, 0xe5, 0x81, 0xa3, 0xa9,
                0x21, 0xdc, 0x2a, 0xd0, 0x74, 0x92, 0xf1, 0x7f, 0xef, 0x33, 0x09, 0x36, 0x43, 0xe6, 0x8d, 0xfc, 0x1f,
                0xb7, 0x0a, 0xb8, 0xda, 0x7b, 0xd1, 0x80, 0xd9, 0x06, 0x16, 0xcb, 0x84, 0x73, 0x00, 0x5a, 0x9b, 0x2d,
                0x40, 0x14, 0x56, 0x97, 0x37, 0x7e, 0x63, 0x98, 0xe2, 0xdc, 0x51, 0xbd, 0x40, 0xe6, 0xa9, 0x2a, 0x53,
                0xe9, 0x82, 0x94, 0xaf, 0x98, 0x41, 0xd1, 0xf6, 0x87, 0xcd, 0x48, 0xba, 0x6e, 0xed, 0xf5, 0x39, 0xa1,
                0x4a, 0xbc, 0x0a, 0xc4, 0xa0, 0x57, 0x1e, 0x93, 0xd8, 0xdb, 0x4a, 0x1e, 0xfb, 0xe7, 0x94, 0xb2, 0x78,
                0xca, 0x97, 0x22, 0xd6, 0xf6, 0x8a, 0x30, 0x39, 0x50, 0x7d, 0x8c, 0x2c, 0x4d, 0xea, 0x91, 0x82, 0x9d,
                0x79,
            },
            DP = new byte[]
            {
                0x04, 0x17, 0xd3, 0x77, 0xd7, 0x05, 0xa9, 0x87, 0xee, 0x84, 0xd8, 0xf1, 0x29, 0xe6, 0x91, 0x6f, 0xe4,
                0x9a, 0xbb, 0x4a, 0xdf, 0x79, 0xba, 0xa8, 0x02, 0x35, 0x63, 0x47, 0x93, 0x9a, 0x02, 0xcf, 0x3d, 0xff,
                0x5a, 0x89, 0xa0, 0xd1, 0xd6, 0x71, 0x59, 0x75, 0x5b, 0x3d, 0xc0, 0xa1, 0x8b, 0x95, 0xbf, 0xc6, 0x43,
                0x41, 0xd7, 0xd9, 0x2c, 0xdd, 0x28, 0xb7, 0x85, 0x42, 0x7a, 0x79, 0x98, 0x88, 0xe1, 0xcb, 0x1f, 0x80,
                0xb7, 0x15, 0x4f, 0xcc, 0x43, 0xd6, 0x46, 0x9a, 0x34, 0x17, 0x95, 0x44, 0x05, 0x75, 0x7a, 0xb3, 0xed,
                0x03, 0x6b, 0x69, 0xcd, 0xbc, 0xc6, 0xb6, 0x83, 0xbd, 0x6a, 0xb5, 0x56, 0x7e, 0xdf, 0x8e, 0x2e, 0x24,
                0xeb, 0x2c, 0x65, 0x13, 0x6a, 0x47, 0x5e, 0x67, 0xb8, 0xee, 0xbe, 0x31, 0x60, 0x66, 0x6d, 0x5f, 0xaf,
                0xaa, 0x05, 0xa1, 0x62, 0x28, 0xaf, 0xd2, 0x67, 0xc3,
            },
            DQ = new byte[]
            {
                0xd7, 0xa6, 0xc3, 0xe9, 0xaf, 0x5a, 0x9f, 0x8d, 0xfa, 0xa5, 0xc1, 0x97, 0x97, 0xbc, 0x25, 0xa8, 0x17,
                0x72, 0x9c, 0x91, 0xac, 0xa2, 0xab, 0x31, 0x4f, 0x14, 0x37, 0x49, 0xf9, 0xf2, 0x58, 0x9c, 0xee, 0x9e,
                0x20, 0x10, 0xc7, 0xad, 0x38, 0x70, 0xaf, 0xdc, 0x4e, 0x9b, 0x9c, 0xa0, 0x88, 0x97, 0xfb, 0xb3, 0x31,
                0xcd, 0xa6, 0x8c, 0x81, 0x17, 0xba, 0x86, 0x30, 0x54, 0x05, 0x0c, 0xc2, 0x9d, 0x94, 0x60, 0xc7, 0x7e,
                0xa0, 0xc8, 0x6e, 0xcd, 0x92, 0xe2, 0x0a, 0x55, 0x4b, 0xb6, 0x2d, 0x3c, 0x48, 0xf1, 0xd6, 0x98, 0x34,
                0x29, 0xa3, 0x47, 0x61, 0xdd, 0xdd, 0x8c, 0xf8, 0xeb, 0xd9, 0x04, 0x9e, 0xd1, 0x1a, 0x52, 0x4b, 0xd7,
                0xe3, 0xc0, 0x35, 0x82, 0xbf, 0x9c, 0x51, 0x84, 0xc1, 0x58, 0x27, 0x33, 0x1a, 0x31, 0xdc, 0x14, 0x10,
                0x4a, 0x14, 0x08, 0xaa, 0x5f, 0xbd, 0x39, 0xb9, 0xe3,
            },
            InverseQ = new byte[]
            {
                0xc3, 0xb6, 0xac, 0xaf, 0x03, 0x37, 0xce, 0x27, 0x48, 0xf7, 0x96, 0xa4, 0xac, 0xec, 0x2d, 0xd1, 0x22,
                0xe5, 0x05, 0xd2, 0x98, 0x5f, 0x35, 0xfc, 0x25, 0x63, 0xa6, 0x8e, 0x44, 0x63, 0x45, 0xef, 0xb1, 0x62,
                0xfb, 0x54, 0xcd, 0xa3, 0x0f, 0xa7, 0xaf, 0xb6, 0x63, 0x7a, 0xb2, 0x9d, 0xff, 0xe7, 0x84, 0x1e, 0x25,
                0xca, 0x6d, 0xa7, 0x41, 0x2a, 0xde, 0x25, 0xdb, 0xa8, 0xa2, 0x7a, 0x2d, 0x3d, 0xc9, 0x4d, 0x5e, 0x0b,
                0x78, 0x19, 0xfe, 0xc0, 0x20, 0x26, 0x72, 0xf4, 0xf5, 0xd2, 0x06, 0x68, 0x8a, 0xe9, 0xb9, 0xd6, 0xc9,
                0x6e, 0x9d, 0x7c, 0x8b, 0x56, 0xa7, 0x84, 0x65, 0xbc, 0xc3, 0xd7, 0xe6, 0xf6, 0x37, 0x1c, 0x68, 0x60,
                0xed, 0x4c, 0xba, 0x2d, 0x05, 0xeb, 0x00, 0xee, 0xe7, 0xf8, 0x52, 0x66, 0x98, 0xeb, 0x16, 0x16, 0x5f,
                0x6a, 0x8f, 0x61, 0x19, 0x2d, 0x96, 0x54, 0xff, 0xf6,
            },
        };

        private static readonly RSAParameters EndEntityRsaParams = new RSAParameters
        {
            Modulus = new byte[]
            {
                0xd8, 0xff, 0xb0, 0xad, 0xe1, 0x4f, 0xf0, 0x1f, 0x07, 0x28, 0x6a, 0x70, 0x2a, 0x8d, 0x92, 0x91, 0xd1,
                0x7f, 0x06, 0x1b, 0x51, 0xa3, 0x82, 0x48, 0x32, 0x67, 0xd5, 0x55, 0x2b, 0x1b, 0x53, 0x51, 0x24, 0x62,
                0xd7, 0xac, 0xb9, 0xf4, 0x27, 0x00, 0xf4, 0x23, 0xbf, 0xa9, 0xb3, 0xc7, 0x13, 0xfb, 0x46, 0x44, 0x4e,
                0xb4, 0x1a, 0x92, 0x4a, 0xfe, 0xc2, 0x39, 0x9b, 0xf3, 0x54, 0xee, 0x63, 0xd5, 0xa6, 0xcc, 0x85, 0xff,
                0xd8, 0x13, 0x0e, 0x77, 0xda, 0x7b, 0x11, 0x49, 0xb9, 0xa0, 0x9b, 0x73, 0x18, 0x2b, 0xae, 0x1d, 0xe1,
                0x8a, 0xfc, 0xfb, 0xd1, 0x6e, 0xa1, 0x8b, 0x7d, 0xb4, 0xde, 0xdc, 0xec, 0x58, 0xd8, 0x5a, 0x25, 0xfc,
                0xd6, 0x19, 0xfb, 0xc5, 0xa4, 0x88, 0x5d, 0x74, 0x7d, 0x5c, 0xeb, 0x64, 0xeb, 0x85, 0x1f, 0xb2, 0x89,
                0xc9, 0xb7, 0xeb, 0x68, 0xa6, 0x94, 0x18, 0xfd, 0x71, 0xce, 0x8d, 0x80, 0x9f, 0x52, 0x99, 0x4d, 0x9a,
                0x7b, 0x22, 0xbf, 0x9e, 0xb8, 0x18, 0x49, 0xda, 0xce, 0xff, 0x24, 0x32, 0x7b, 0x61, 0x0d, 0x4b, 0xbd,
                0x4e, 0xfe, 0xd3, 0x96, 0x49, 0xe1, 0xbf, 0xc2, 0x4f, 0x93, 0x7d, 0xcf, 0x35, 0xce, 0x80, 0xd2, 0x91,
                0x31, 0xa2, 0xd6, 0xd7, 0xe2, 0xc2, 0x9b, 0x90, 0xae, 0xfa, 0x4c, 0x11, 0x0f, 0x9c, 0xf7, 0x59, 0xea,
                0x81, 0x6b, 0xee, 0x4b, 0x43, 0x28, 0x88, 0x7e, 0x2c, 0xd8, 0xbd, 0x40, 0xc1, 0x2f, 0xf7, 0x78, 0x1d,
                0x63, 0x78, 0x15, 0x88, 0x38, 0x5b, 0x37, 0xfb, 0xdf, 0x12, 0x43, 0xe6, 0x0b, 0x26, 0xd5, 0xfd, 0x68,
                0xa6, 0x18, 0x05, 0x19, 0x26, 0x95, 0xed, 0x08, 0x37, 0xa8, 0xae, 0x6a, 0x83, 0x87, 0xc7, 0x68, 0xb6,
                0x16, 0x44, 0x8a, 0xec, 0x21, 0x04, 0x71, 0x95, 0x96, 0x38, 0x08, 0x77, 0x51, 0xdc, 0xb4, 0xf7, 0xf3,
                0xf9,
            },
            Exponent = new byte[] { 0x01, 0x00, 0x01 },
            P = new byte[]
            {
                0xf2, 0x53, 0xd8, 0xc9, 0xe5, 0xd1, 0xc2, 0x58, 0xce, 0x8e, 0xdb, 0xbf, 0xfc, 0x8d, 0x5e, 0x0a, 0xc5,
                0xfd, 0xdb, 0x4b, 0x27, 0x99, 0x23, 0x01, 0x4c, 0x22, 0x65, 0xdf, 0xfe, 0xc7, 0x29, 0x8a, 0xa7, 0xae,
                0xe4, 0xdc, 0xc4, 0xfa, 0x6d, 0xa5, 0x7d, 0x73, 0xc3, 0xee, 0x1b, 0xa0, 0x06, 0x30, 0x50, 0xdd, 0x71,
                0xff, 0xd6, 0x08, 0x17, 0xe5, 0x98, 0xfe, 0x1c, 0xb7, 0x0b, 0xef, 0x6f, 0xf5, 0xbe, 0xaa, 0xb8, 0x53,
                0x2c, 0x26, 0xe8, 0xe8, 0x39, 0xaf, 0xd2, 0x8c, 0xe0, 0x87, 0x08, 0x9c, 0xf3, 0x60, 0xd8, 0xb1, 0xd9,
                0x66, 0x93, 0xb9, 0x35, 0x11, 0xf5, 0xb6, 0xe2, 0x53, 0xb5, 0xa3, 0xdb, 0x1d, 0x75, 0x5c, 0xcb, 0x7c,
                0x3b, 0x91, 0x2f, 0x01, 0xf1, 0x46, 0x6b, 0xe7, 0xc0, 0x08, 0xa5, 0x89, 0x8a, 0xac, 0x5b, 0xa3, 0x78,
                0x12, 0x8e, 0x62, 0x0f, 0x3a, 0xf9, 0x88, 0x3d, 0x47,
            },
            Q = new byte[]
            {
                0xe5, 0x3d, 0xff, 0x6a, 0xcd, 0xf6, 0xb3, 0x2a, 0xd7, 0xee, 0x3a, 0x63, 0x30, 0x1c, 0x39, 0x51, 0x22,
                0x78, 0xed, 0x28, 0xb8, 0x11, 0x94, 0x78, 0xfa, 0x70, 0xd6, 0xdc, 0xa2, 0xa5, 0x5f, 0x90, 0x4b, 0x08,
                0x41, 0x3e, 0x73, 0x72, 0xe0, 0xde, 0x03, 0x3f, 0xb5, 0x07, 0xcc, 0x36, 0x46, 0x18, 0x6a, 0x4f, 0x8a,
                0x30, 0xdd, 0xf9, 0x28, 0xb0, 0x3a, 0xd7, 0x9b, 0x04, 0x14, 0x7b, 0xab, 0x2b, 0xa9, 0xb9, 0x61, 0xc8,
                0xcf, 0x59, 0x5c, 0x25, 0xdb, 0x05, 0x12, 0x97, 0x68, 0x7f, 0xad, 0x0c, 0xd2, 0x91, 0xfe, 0xa7, 0x10,
                0x1c, 0xa0, 0xa6, 0x22, 0xc3, 0xa8, 0x2c, 0x82, 0xef, 0xd7, 0xd7, 0x47, 0xa7, 0xd8, 0x74, 0x12, 0xb0,
                0xcd, 0x96, 0x6b, 0x9a, 0x50, 0xf0, 0xb1, 0xb6, 0xf1, 0x7c, 0x9c, 0xc5, 0x1a, 0x62, 0x24, 0x87, 0x99,
                0x85, 0xa3, 0xad, 0x39, 0x36, 0xc3, 0xc9, 0xe4, 0xbf,
            },
            D = new byte[]
            {
                0xa6, 0xa4, 0xcd, 0x70, 0xea, 0xfb, 0xf1, 0xa2, 0x52, 0x63, 0xe6, 0x41, 0x97, 0x5c, 0x3b, 0x78, 0x02,
                0x13, 0x73, 0x84, 0x1d, 0x50, 0xdd, 0x27, 0x46, 0x96, 0x58, 0xcd, 0x5c, 0x1a, 0x53, 0x04, 0x98, 0x55,
                0xd3, 0xdd, 0x50, 0xbc, 0xc0, 0x0b, 0x4a, 0x71, 0xfd, 0xa9, 0x7c, 0x67, 0x60, 0xdf, 0xf2, 0x19, 0x58,
                0xfb, 0x95, 0x00, 0x4d, 0xd9, 0x91, 0x1c, 0x9e, 0xb7, 0xe2, 0xbc, 0x64, 0x2c, 0xda, 0x38, 0x6c, 0x9b,
                0x8a, 0xbb, 0x2f, 0xbc, 0x39, 0x2b, 0x93, 0x9e, 0x33, 0x90, 0xb4, 0x70, 0x51, 0xda, 0x91, 0x8f, 0x5e,
                0xfa, 0xd6, 0xc7, 0x28, 0x11, 0xb6, 0xbb, 0xa1, 0xe0, 0xf9, 0xd9, 0x5d, 0x23, 0xe9, 0x9a, 0x69, 0x5b,
                0xde, 0xab, 0xfb, 0x9e, 0xcf, 0x78, 0xed, 0x94, 0x1d, 0x05, 0xf3, 0xbb, 0xff, 0xe6, 0xae, 0xed, 0xf4,
                0x44, 0xd6, 0x1a, 0x51, 0xb6, 0xc3, 0x3a, 0xe1, 0xbe, 0x4f, 0x44, 0xfa, 0x14, 0x4f, 0x3c, 0x81, 0x06,
                0x1f, 0x6d, 0xcd, 0x57, 0x14, 0x40, 0x01, 0x91, 0xd4, 0xc6, 0x58, 0xf6, 0x6b, 0x2c, 0x3e, 0x81, 0x6a,
                0x96, 0x4c, 0x3a, 0x46, 0xf7, 0x89, 0x30, 0xa0, 0x40, 0x25, 0x98, 0xb5, 0xc4, 0xea, 0x0d, 0x87, 0x06,
                0x27, 0xe1, 0x9e, 0x76, 0x70, 0xb1, 0xcd, 0xf1, 0xa2, 0x86, 0x90, 0x61, 0x6b, 0x92, 0xc6, 0xe2, 0xa9,
                0xff, 0x80, 0x54, 0x11, 0xed, 0x89, 0x5a, 0x29, 0x02, 0x8e, 0x74, 0x5b, 0xb3, 0x33, 0x37, 0x10, 0x19,
                0x7f, 0x06, 0x1c, 0x22, 0x7f, 0x67, 0xca, 0xf6, 0xba, 0x6f, 0x8f, 0xf3, 0xd8, 0xd7, 0x81, 0xa6, 0xf0,
                0x7c, 0x87, 0x79, 0xe5, 0x9c, 0xd5, 0xbc, 0xd3, 0x48, 0x6f, 0x34, 0x1f, 0x8b, 0x32, 0xef, 0xd9, 0xca,
                0xf0, 0x52, 0xb9, 0x98, 0x3f, 0x6f, 0x23, 0x63, 0xb2, 0xcf, 0xf0, 0xde, 0xda, 0x84, 0xac, 0x04, 0x4a,
                0xd5,
            },
            DP = new byte[]
            {
                0xbb, 0x39, 0xf3, 0x16, 0x52, 0xdd, 0x55, 0x06, 0x1e, 0x59, 0x9c, 0x09, 0x62, 0x8c, 0xaa, 0xeb, 0x31,
                0xfc, 0x28, 0x11, 0x91, 0xff, 0xac, 0x5f, 0x15, 0x3e, 0xc2, 0x6d, 0x65, 0x40, 0xe5, 0xa4, 0xbe, 0x57,
                0xcf, 0x75, 0x8f, 0x2f, 0x59, 0xc5, 0xf1, 0xfe, 0x9e, 0x93, 0xfa, 0x7e, 0x12, 0x2a, 0x04, 0x60, 0x83,
                0xf2, 0xc1, 0xa0, 0x31, 0x2e, 0x70, 0x9d, 0x6c, 0xfc, 0x34, 0x59, 0x83, 0xac, 0x5f, 0xeb, 0x31, 0x4c,
                0xf9, 0xa0, 0xfa, 0x74, 0x6a, 0x15, 0xa1, 0x5c, 0xbd, 0x21, 0x37, 0x93, 0x64, 0x2b, 0x20, 0x61, 0x90,
                0xf1, 0xc3, 0x12, 0xe6, 0xa1, 0x00, 0xb2, 0x93, 0x7d, 0x4f, 0xaa, 0xd0, 0xe1, 0x9a, 0xca, 0xde, 0x61,
                0x16, 0xf8, 0xde, 0x53, 0xe6, 0xe1, 0x9c, 0xff, 0x4a, 0x8c, 0xa3, 0xb1, 0x78, 0x16, 0x21, 0x1b, 0x54,
                0xeb, 0x29, 0x5d, 0x34, 0x1d, 0x41, 0xac, 0x74, 0x83,
            },
            DQ = new byte[]
            {
                0xa8, 0x89, 0x3a, 0x1d, 0x15, 0xab, 0x87, 0xf1, 0xb9, 0xaa, 0xc5, 0x76, 0x62, 0xca, 0x7d, 0x41, 0x2f,
                0x2c, 0xe4, 0x7f, 0x09, 0x44, 0xb3, 0x79, 0x75, 0xf6, 0x3b, 0xa1, 0x1e, 0x5a, 0xa2, 0xb5, 0x7c, 0xd4,
                0x66, 0xd3, 0x39, 0x21, 0x7e, 0x3c, 0xfa, 0xfa, 0x7d, 0x67, 0x6c, 0x35, 0x82, 0xb7, 0x34, 0x81, 0xa1,
                0xc1, 0x67, 0x90, 0x64, 0xdf, 0x9b, 0x83, 0x23, 0xce, 0x8e, 0x18, 0x95, 0xb1, 0x96, 0x28, 0x5a, 0xc1,
                0xbd, 0xdf, 0x9e, 0xa5, 0x9e, 0x2e, 0x4e, 0x8a, 0xce, 0x22, 0xff, 0xe0, 0xeb, 0x76, 0xb6, 0x57, 0xb0,
                0xba, 0xbb, 0x49, 0x29, 0x49, 0xdb, 0x7c, 0x4e, 0x0f, 0x73, 0x0a, 0x2c, 0xfe, 0x33, 0x5e, 0xb2, 0xd7,
                0x15, 0x6e, 0xbf, 0x51, 0x46, 0xac, 0x8e, 0x9b, 0x47, 0x53, 0x2c, 0x16, 0xa4, 0xdc, 0xfe, 0xaa, 0x4a,
                0xae, 0x3b, 0xb5, 0x80, 0xd8, 0xc8, 0x7c, 0xc8, 0x15,
            },
            InverseQ = new byte[]
            {
                0x1b, 0x05, 0x2a, 0x58, 0xa1, 0xe2, 0x3b, 0x15, 0x4b, 0xa8, 0x52, 0x6b, 0x1e, 0xf4, 0x56, 0x90, 0x03,
                0xef, 0xa7, 0x32, 0x64, 0x7c, 0xf1, 0xb8, 0xbe, 0x99, 0xfd, 0xc0, 0x6d, 0x9b, 0xc8, 0x0d, 0xe1, 0xe6,
                0xd7, 0x61, 0xf5, 0x66, 0x61, 0xd9, 0x6b, 0x54, 0xf4, 0x59, 0x6e, 0x11, 0xf0, 0x8b, 0xf9, 0x6a, 0x02,
                0x20, 0xbe, 0x49, 0xe2, 0x38, 0x71, 0xf7, 0xf2, 0x8d, 0x24, 0x3b, 0x8c, 0x6a, 0x2a, 0x03, 0x3f, 0xbb,
                0x38, 0x67, 0x18, 0xe5, 0x48, 0x5c, 0x48, 0x29, 0x91, 0x12, 0x45, 0xb2, 0xb1, 0xe9, 0xfc, 0x7d, 0xc2,
                0x0e, 0xb9, 0x7a, 0xe3, 0x61, 0xf7, 0x51, 0x42, 0xcd, 0x87, 0xe0, 0x96, 0x0e, 0xcc, 0x24, 0x89, 0xd8,
                0xba, 0xb9, 0x7f, 0x24, 0x10, 0xf9, 0x50, 0x23, 0x79, 0x9c, 0x75, 0x62, 0x79, 0x22, 0xe0, 0x9d, 0x19,
                0x49, 0x2f, 0x61, 0x0c, 0x91, 0x83, 0x3c, 0x55, 0x51,
            },
        };

        public static readonly X500DistinguishedName ExpiredRootCaName =
            new X500DistinguishedName("CN=TEST_CERT_Expired_root_now_valid_intermediate_CA");

        public static readonly X500DistinguishedName NewRootCaName =
            new X500DistinguishedName("CN=TEST_CERT_New_root_CA");

        public static readonly X500DistinguishedName SecondIntermediateCaName =
            new X500DistinguishedName("CN=TEST_CERT_Second_Intermediate_CA");

        public static readonly X500DistinguishedName EndEntityName =
            new X500DistinguishedName("CN=some.server.invalid");

        private static DateTimeOffset GetUtcNowWithSecondResolution()
        {
            DateTime now = DateTime.UtcNow;
            return new DateTime(
                now.Year,
                now.Month,
                now.Day,
                now.Hour,
                now.Minute,
                now.Second,
                DateTimeKind.Utc
            ).ToUniversalTime();
        }

        private static void PrintCertChain(X509ChainElementCollection chainElements)
        {
            foreach ((X509ChainElement chainElem, int idx) in chainElements.Cast<X509ChainElement>()
                .Select((x, i) => (x, i)))
            {
                string errorString = string.Join(
                    "\n\t",
                    string.Join("\n\t", chainElem.ChainElementStatus.Select(x => x.StatusInformation)),
                    chainElem.Information
                ).Trim();

                Console.WriteLine(
                    "{0}: {1}, {2} - {3}{4}",
                    idx,
                    chainElem.Certificate.Subject,
                    new DateTimeOffset(chainElem.Certificate.NotBefore.ToUniversalTime()),
                    new DateTimeOffset(chainElem.Certificate.NotAfter.ToUniversalTime()),
                    !string.IsNullOrEmpty(errorString)
                        ? string.Format(CultureInfo.InvariantCulture, "\n\t{0}", errorString)
                        : string.Empty
                );
            }
        }

        private static CertificateRequest CreateCertificateRequest(
            RSA key,
            X500DistinguishedName subjectName,
            bool isCa
        )
        {
            var req = new CertificateRequest(
                subjectName,
                key,
                HashAlgorithmName.SHA256,
                RSASignaturePadding.Pkcs1
            );

            req.CertificateExtensions.Add(
                new X509SubjectKeyIdentifierExtension(
                    key: req.PublicKey,
                    critical: false
                )
            );

            X509KeyUsageFlags keyUsages = X509KeyUsageFlags.DigitalSignature;
            if (isCa)
            {
                keyUsages |= X509KeyUsageFlags.KeyCertSign;
            }

            req.CertificateExtensions.Add(
                new X509KeyUsageExtension(
                    keyUsages: keyUsages,
                    critical: true
                )
            );

            req.CertificateExtensions.Add(
                new X509BasicConstraintsExtension(
                    certificateAuthority: isCa,
                    hasPathLengthConstraint: false,
                    pathLengthConstraint: 0,
                    critical: true
                )
            );

            if (!isCa)
            {
                var extendedKeyUsages = new OidCollection
                {
                    new Oid("1.3.6.1.5.5.7.3.1", "id-kp-serverAuth"),
                    new Oid("1.3.6.1.5.5.7.3.2", "id-kp-clientAuth"),
                };

                req.CertificateExtensions.Add(
                    new X509EnhancedKeyUsageExtension(
                        enhancedKeyUsages: extendedKeyUsages,
                        critical: true
                    )
                );
            }

            return req;
        }

        private static X509Certificate2 CreateCert(
            RSA key,
            X500DistinguishedName subjectName,
            byte[] serialNumber,
            RSA issuerKey,
            X500DistinguishedName issuerName,
            DateTimeOffset notBefore,
            DateTimeOffset notAfter,
            bool isCa
        )
        {
            var signatureGenerator = X509SignatureGenerator.CreateForRSA(issuerKey, RSASignaturePadding.Pkcs1);

            CertificateRequest req = CreateCertificateRequest(key, subjectName, isCa);

            using (X509Certificate2 certWithoutKey = req.Create(
                issuerName: issuerName,
                generator: signatureGenerator,
                notBefore: notBefore,
                notAfter: notAfter,
                serialNumber: serialNumber
            ))
            {
                using (var certWithKey = certWithoutKey.CopyWithPrivateKey(key))
                {
                    // https://github.com/dotnet/runtime/issues/23749#issuecomment-388231655
                    return new X509Certificate2(
                        certWithKey.Export(X509ContentType.Pkcs12),
                        (string)null,
                        X509KeyStorageFlags.Exportable
                    );
                }
            }
        }

        private static void GenerateCerts(
            out X509Certificate2 expiredRootCa,
            out X509Certificate2 newRootCa,
            out X509Certificate2 firstIntermediate,
            out X509Certificate2 secondIntermediate,
            out X509Certificate2 endEntity
        )
        {
            DateTimeOffset now = GetUtcNowWithSecondResolution();

            RSA newRootCaKey = RSA.Create(NewRootCaRsaParams);
            RSA firstIntermediateKey = RSA.Create(FirstIntermediateRsaParams);
            RSA secondIntermediateKey = RSA.Create(SecondIntermediateRsaParams);
            RSA endEntityKey = RSA.Create(EndEntityRsaParams);

            expiredRootCa = CreateCert(
                key: firstIntermediateKey,
                subjectName: ExpiredRootCaName,
                serialNumber: new byte[] { 0x02 },
                issuerKey: firstIntermediateKey,
                issuerName: ExpiredRootCaName,
                notBefore: now.AddYears(-10),
                notAfter: now.AddMinutes(-2),
                isCa: true
            );

            newRootCa = CreateCert(
                key: newRootCaKey,
                subjectName: NewRootCaName,
                serialNumber: new byte[] { 0x01 },
                issuerKey: newRootCaKey,
                issuerName: NewRootCaName,
                notBefore: now.AddYears(-20),
                notAfter: now.AddYears(20),
                isCa: true
            );

            firstIntermediate = CreateCert(
                key: firstIntermediateKey,
                subjectName: ExpiredRootCaName,
                serialNumber: new byte[] { 0x03 },
                issuerKey: newRootCaKey,
                issuerName: NewRootCaName,
                notBefore: now.AddYears(-20),
                notAfter: now.AddYears(20),
                isCa: true
            );

            secondIntermediate = CreateCert(
                key: secondIntermediateKey,
                subjectName: SecondIntermediateCaName,
                serialNumber: new byte[] { 0x04 },
                issuerKey: firstIntermediateKey,
                issuerName: ExpiredRootCaName,
                notBefore: now.AddYears(-2),
                notAfter: now.AddYears(10),
                isCa: true
            );

            endEntity = CreateCert(
                key: endEntityKey,
                subjectName: EndEntityName,
                serialNumber: new byte[] { 0x05 },
                issuerKey: secondIntermediateKey,
                issuerName: SecondIntermediateCaName,
                notBefore: now.AddYears(-2),
                notAfter: now.AddYears(10),
                isCa: false
            );
        }

        private static string CertToPem(X509Certificate2 cert)
        {
            string base64 = Convert.ToBase64String(cert.Export(X509ContentType.Cert));
            return string.Format(
                CultureInfo.InvariantCulture,
                "-----BEGIN CERTIFICATE-----\n{0}\n-----END CERTIFICATE-----\n",
                string.Join(
                    "\n",
                    Enumerable.Range(
                        0,
                        (base64.Length % 64) == 0 ? base64.Length / 64 : (base64.Length / 64) + 1
                    ).Select(
                        i => base64.Substring(i * 64, (i * 64 + 64 <= base64.Length) ? 64 : base64.Length - i * 64)
                    )
                )
            );
        }

        private static void InstallCertificates(
            X509Certificate2 expiredRootCa,
            X509Certificate2 newRootCa,
            X509Certificate2 firstIntermediate,
            X509Certificate2 secondIntermediate
        )
        {
            string exeDir = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
            string sslCertsFilePath = Path.Combine(exeDir, "sslCerts.pem");

            string envSslCertDir = Environment.GetEnvironmentVariable("SSL_CERT_DIR");
            string envSslCertFile = Environment.GetEnvironmentVariable("SSL_CERT_FILE");

            if (envSslCertDir != string.Empty || envSslCertFile != sslCertsFilePath)
            {
                Console.Error.WriteLine(
                    "Program needs to be run with SSL_CERT_DIR=\"\" SSL_CERT_FILE=\"{0}\"",
                    sslCertsFilePath
                );
                throw new InvalidOperationException("OpenSSL environment variables must be set");
            }

            using (var fileStream = File.Open(sslCertsFilePath, FileMode.Create, FileAccess.Write, FileShare.None))
            {
                using (var streamWriter = new StreamWriter(fileStream))
                {
                    foreach (var cert in new[] { expiredRootCa, newRootCa, firstIntermediate, secondIntermediate })
                    {
                        streamWriter.Write(CertToPem(cert));
                        streamWriter.Flush();
                    }
                }
            }
        }

        private static void DumpEndEntityCertificate(X509Certificate2 endEntity)
        {
            string exeDir = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
            string endEntityFilePath = Path.Combine(exeDir, "endEntity.pem");
            using (var fileStream = File.Open(
                endEntityFilePath,
                FileMode.Create,
                FileAccess.Write,
                FileShare.None
            ))
            {
                using (var streamWriter = new StreamWriter(fileStream))
                {
                    streamWriter.Write(CertToPem(endEntity));
                    streamWriter.Flush();
                }
            }
        }

        private static void TestOpenSslChainBuilding(
            X509Certificate2 expiredRootCa,
            X509Certificate2 newRootCa,
            X509Certificate2 firstIntermediate,
            X509Certificate2 secondIntermediate,
            X509Certificate2 endEntity
        )
        {
            InstallCertificates(expiredRootCa, newRootCa, firstIntermediate, secondIntermediate);
            DumpEndEntityCertificate(endEntity);

            Console.WriteLine("Testing chain building...");
            Console.WriteLine();

            var localChain = new X509Chain(true)
            {
                ChainPolicy = new X509ChainPolicy
                {
                    RevocationMode = X509RevocationMode.NoCheck,
                    VerificationTime = DateTime.Now,
                },
            };
            localChain.Build(endEntity);
            PrintCertChain(localChain.ChainElements);
        }

        private static void Main()
        {
            GenerateCerts(
                out X509Certificate2 expiredRootCa,
                out X509Certificate2 newRootCa,
                out X509Certificate2 firstIntermediate,
                out X509Certificate2 secondIntermediate,
                out X509Certificate2 endEntity
            );

            TestOpenSslChainBuilding(
                expiredRootCa,
                newRootCa,
                firstIntermediate,
                secondIntermediate,
                endEntity
            );
        }
    }
}

The test application produces the following output:

Testing chain building...

0: CN=some.server.invalid, 10/27/2018 10:01:07 +00:00 - 10/27/2030 10:01:07 +00:00
1: CN=TEST_CERT_Second_Intermediate_CA, 10/27/2018 10:01:07 +00:00 - 10/27/2030 10:01:07 +00:00
2: CN=TEST_CERT_Expired_root_now_valid_intermediate_CA, 10/27/2010 10:01:07 +00:00 - 10/27/2020 09:59:07 +00:00
        certificate has expired

The test application also dumps the generated CA certificates and end-entity certificate to two files sslCerts.pem and endEntity.pem. Running openssl verify with those files gives us the following:

$ openssl verify -verbose -show_chain -CAfile sslCerts.pem endEntity.pem
endEntity.pem: OK
Chain:
depth=0: CN = some.server.invalid (untrusted)
depth=1: CN = TEST_CERT_Second_Intermediate_CA
depth=2: CN = TEST_CERT_Expired_root_now_valid_intermediate_CA
depth=3: CN = TEST_CERT_New_root_CA

In other words, openssl verify is able to resolve the right certificate chain, whereas .NET using openssl is not.

Configuration

Seen on .NET Core 3.1.8 on Linux, using OpenSSL 1.1.1f 31 Mar 2020.

@Dotnet-GitSync-Bot Dotnet-GitSync-Bot added area-System.Security untriaged New issue has not been triaged by the area owner labels Oct 27, 2020
@ghost
Copy link

ghost commented Oct 27, 2020

Tagging subscribers to this area: @bartonjs, @vcsjones, @krwq, @jeffhandley
See info in area-owners.md if you want to be subscribed.

@bartonjs
Copy link
Member

bartonjs commented Jul 6, 2021

We should look at this for 7, since we've done a lot of chain rework since 3.1.

Since we have the CustomRootTrust mode on X509Chain now we can ignore all the system installation pieces. And we can use CertificateRequest to build the test chain on the fly.

@bartonjs
Copy link
Member

bartonjs commented Aug 2, 2022

I looked into this now to hope to say that our chain build rewrite for 5 solved this, but it hasn't. Using CustomTrustStore I tried with leftRoot first, or rightRoot first, and both of them terminated on the expired root.

I'm guessing, without looking at code to verify, that when we do our first chain build we say that expiration doesn't matter (with the intent of moving on past an expired cert to find the full chain), then we lock in whatever root it said it found and build again with an error drain.

So that might mean we want to turn our first build into an even more "golden success only", and turn on expiration. If that fails with a NotTimeValid error then try again with revocation disabled. But, we're too close to the release of 7 to make that change now, since it wasn't something that got newly broken in this release... so moving the target out.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants