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

Caching of SCHANNEL_CRED may lead to SslStream using expired certificate chain #43879

Closed
egraff opened this issue Oct 27, 2020 · 6 comments · Fixed by #66334
Closed

Caching of SCHANNEL_CRED may lead to SslStream using expired certificate chain #43879

egraff opened this issue Oct 27, 2020 · 6 comments · Fixed by #66334

Comments

@egraff
Copy link

egraff commented Oct 27, 2020

Description

The static SslSessionsCache used by SecureChannel -- which in turn is used by SslStream -- to cache SCHANNEL_CRED handles is problematic, because the certificate chain for the end-entity certificate used by SslStream is only built when the SCHANNEL_CRED handle is first acquired, through a call to AcquireCredentialsHandle. If the root CA certificate or one of the intermediate CA certificates expire (this actually happened IRL quite recently), then all SslStream instances created for the rest of the entire lifetime of the process for the same end-entity certificate (for example a Kestrel-based ASP.Net Core web server) will continue to use the expired certificate chain.

Configuration

Seen on .NET on Windows. I have reproduced the problem on .NET Framework 4.8, .NET Core 2.1 and .NET Core 3.1.

Detailed reproduction

This problem was first observed in real life, when the Sectigo AddTrust External CA Root expired on May 30, 2020. A long-living web server serving the following certificate chain (end-entity certificate excluded) continued to serve the same, expired chain after the AddTrust CA certificate expired.

Subject Serial number
C=SE, O=AddTrust AB, OU=AddTrust External TTP Network, CN=AddTrust External CA Root
C=US, ST=New Jersey, L=Jersey City, O=The USERTRUST Network, CN=USERTrust RSA Certification Authority 13:ea:28:70:5b:f4:ec:ed:0c:36:63:09:80:61:43:36
C=GB, ST=Greater Manchester, L=Salford, O=Sectigo Limited, CN=Sectigo RSA Domain Validation Secure Server CA 7d:5b:51:26:b4:76:ba:11:db:74:16:0b:bc:53:0d:a7

The USERTrust intermediate certificate in this chain is a cross-signed CA, which also has a valid root CA certificate, so the expected certificate chain to be returned from the server after the AddTrust certificate expired would be this:

Subject Serial number
C=US, ST=New Jersey, L=Jersey City, O=The USERTRUST Network, CN=USERTrust RSA Certification Authority 01:fd:6d:30:fc:a3:ca:51:a8:1b:bc:64:0e:35:03:2d
C=GB, ST=Greater Manchester, L=Salford, O=Sectigo Limited, CN=Sectigo RSA Domain Validation Secure Server CA 7d:5b:51:26:b4:76:ba:11:db:74:16:0b:bc:53:0d:a7

I have reproduced this issue synthetically, with a stand-alone .NET executable. The application generates the following certificates:

    Old root CA (CN=TEST_CERT_Old_root_CA)
                   |
                   v
Old first intermediate (CN=TEST_CERT_New_root_CA)     New root CA (CN=TEST_CERT_New_root_CA)
               |                                            |
               v                                            v
           Second intermediate (CN=TEST_CERT_Second_Intermediate_CA)
                      |
                      v
      End entity (CN=some.server.invalid)

where Old first intermediate has the same subject name and private key as New root CA (i.e. the CA is cross-signed by Old root CA). Old root CA and Old first intermediate are set to expire in 2 minutes, but the notBefore date is set to be more recent than New root CA to trick CryptoAPI to prefer that certificate chain initially. After this is done, the root and intermediate CA certificates are installed in the system's certificate stores.

After the certificates are setup, the test first builds a certificate chain explicitly, using X509Chain.Build(). Then, the test runs a loop where it creates a new web server SslStream for each iteration, and logs the certificate chain that is received by a connecting web client (note that the root certificate is not included, as it is not part of the TLS Certificate message). There is a 30 seconds sleep between each loop iteration. Finally, after all the loop iterations have run, the test builds another certificate chain explicitly, again using X509Chain.Build().

Expand to see test code included below:
namespace SslStreamCertChainProblemExternalRepro
{
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Linq;
    using System.Net;
    using System.Net.Security;
    using System.Net.Sockets;
    using System.Security.Authentication;
    using System.Security.Cryptography;
    using System.Security.Cryptography.X509Certificates;
    using System.Threading;
    using System.Threading.Tasks;

    using Org.BouncyCastle.Crypto.Tls;
    using Org.BouncyCastle.Security;

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

    internal class TlsClient : DefaultTlsClient
    {
        private readonly Action<Certificate> onNotifyServerCert;

        /// <inheritdoc />
        public TlsClient(Action<Certificate> onNotifyServerCert)
        {
            this.onNotifyServerCert = onNotifyServerCert;
        }

        /// <inheritdoc />
        public override Org.BouncyCastle.Crypto.Tls.TlsAuthentication GetAuthentication()
        {
            return new TlsAuthentication(onNotifyServerCert);
        }

        private class TlsAuthentication : Org.BouncyCastle.Crypto.Tls.TlsAuthentication
        {
            private readonly Action<Certificate> onNotifyServerCert;

            public TlsAuthentication(Action<Certificate> onNotifyServerCert)
            {
                this.onNotifyServerCert = onNotifyServerCert;
            }

            /// <inheritdoc />
            public void NotifyServerCertificate(Certificate serverCertificate)
            {
                onNotifyServerCert(serverCertificate);
            }

            /// <inheritdoc />
            public TlsCredentials GetClientCredentials(
                Org.BouncyCastle.Crypto.Tls.CertificateRequest certificateRequest
            )
            {
                return null;
            }
        }
    }

    internal class Program
    {
        private static readonly RSAParameters OldRootCaRsaParams = 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 NewRootCaRsaParams = 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 OldRootCaName =
            new X500DistinguishedName("CN=TEST_CERT_Old_root_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 void PrintCerts(IEnumerable<X509Certificate2> certs)
        {
            foreach ((X509Certificate2 cert, int idx) in certs.Select((x, i) => (x, i)))
            {
                Console.WriteLine(
                    "{0}: {1}, {2} - {3}",
                    idx,
                    cert.Subject,
                    new DateTimeOffset(cert.NotBefore.ToUniversalTime()),
                    new DateTimeOffset(cert.NotAfter.ToUniversalTime())
                );
            }
        }

        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 oldRootCa,
            out X509Certificate2 newRootCa,
            out X509Certificate2 oldFirstIntermediate,
            out X509Certificate2 secondIntermediate,
            out X509Certificate2 endEntity
        )
        {
            DateTimeOffset now = GetUtcNowWithSecondResolution();

            RSA oldRootCaKey = RSA.Create(OldRootCaRsaParams);
            RSA newRootCaKey = RSA.Create(NewRootCaRsaParams);
            RSA secondIntermediateKey = RSA.Create(SecondIntermediateRsaParams);
            RSA endEntityKey = RSA.Create(EndEntityRsaParams);

            /*
             *         Old root CA
             *                 |
             *                 v
             *  Old first intermediate     New root CA
             *                 |             |
             *                 v             v
             *               Second intermediate
             *                        |
             *                        v
             *                   End entity  
             */

            oldRootCa = CreateCert(
                key: oldRootCaKey,
                subjectName: OldRootCaName,
                serialNumber: new byte[] { 0x01 },
                issuerKey: oldRootCaKey,
                issuerName: OldRootCaName,
                notBefore: now.AddYears(-10),
                notAfter: now.AddMinutes(2),
                isCa: true
            );

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

            oldFirstIntermediate = CreateCert(
                key: newRootCaKey,
                subjectName: NewRootCaName,
                serialNumber: new byte[] { 0x03 },
                issuerKey: oldRootCaKey,
                issuerName: OldRootCaName,
                notBefore: now.AddYears(-10),
                notAfter: now.AddMinutes(2),
                isCa: true
            );

            secondIntermediate = CreateCert(
                key: secondIntermediateKey,
                subjectName: SecondIntermediateCaName,
                serialNumber: new byte[] { 0x04 },
                issuerKey: newRootCaKey,
                issuerName: NewRootCaName,
                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 void PurgeCertificateFromStore(X509Store store, string subjectName)
        {
            X509Certificate2Collection found = store.Certificates.Find(
                X509FindType.FindBySubjectDistinguishedName,
                subjectName,
                validOnly: false
            );
            if (found.Count > 0)
            {
                store.RemoveRange(found);
            }
        }

        private static void InstallCertificates(
            X509Certificate2 oldRootCa,
            X509Certificate2 newRootCa,
            X509Certificate2 oldFirstIntermediate,
            X509Certificate2 secondIntermediate
        )
        {
            const StoreLocation storeLocation = StoreLocation.LocalMachine;

            using (var intermediateStore = new X509Store(
                StoreName.CertificateAuthority,
                storeLocation
            ))
            using (var rootCaStore = new X509Store(StoreName.Root, storeLocation))
            {
                intermediateStore.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadWrite);
                rootCaStore.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadWrite);

                intermediateStore.Add(oldFirstIntermediate);
                intermediateStore.Add(secondIntermediate);

                rootCaStore.Add(oldRootCa);
                rootCaStore.Add(newRootCa);
            }
        }

        private static void PurgeCertificates(
            X509Certificate2 oldRootCa,
            X509Certificate2 newRootCa,
            X509Certificate2 oldFirstIntermediate,
            X509Certificate2 secondIntermediate
        )
        {
            const StoreLocation storeLocation = StoreLocation.LocalMachine;

            using (var intermediateStore = new X509Store(
                StoreName.CertificateAuthority,
                storeLocation
            ))
            using (var rootCaStore = new X509Store(StoreName.Root, storeLocation))
            {
                intermediateStore.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadWrite);
                rootCaStore.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadWrite);

                PurgeCertificateFromStore(intermediateStore, oldFirstIntermediate.Subject);
                PurgeCertificateFromStore(intermediateStore, secondIntermediate.Subject);
                PurgeCertificateFromStore(rootCaStore, oldRootCa.Subject);
                PurgeCertificateFromStore(rootCaStore, newRootCa.Subject);
            }
        }

        private static async Task TestSslStreamChainBuildingAsync(
            X509Certificate2 oldRootCa,
            X509Certificate2 newRootCa,
            X509Certificate2 oldFirstIntermediate,
            X509Certificate2 secondIntermediate,
            X509Certificate2 endEntity
        )
        {
            try
            {
                PurgeCertificates(oldRootCa, newRootCa, oldFirstIntermediate, secondIntermediate);
                InstallCertificates(oldRootCa, newRootCa, oldFirstIntermediate, secondIntermediate);

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

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

                Console.WriteLine("Starting test...");

                TcpListener serverListenSocket = new TcpListener(IPAddress.Loopback, 0);
                serverListenSocket.Start();

                for (int loopCnt = 0; loopCnt < 7; loopCnt++)
                {
                    Console.WriteLine();
                    Console.WriteLine("Chain #{0}, time is now: {1}", loopCnt, DateTimeOffset.UtcNow);

                    Task<TcpClient> serverSocketTask = serverListenSocket.AcceptTcpClientAsync();
                    using (var clientSocket = new TcpClient())
                    {
                        await clientSocket.ConnectAsync(
                            IPAddress.Loopback,
                            ((IPEndPoint)serverListenSocket.LocalEndpoint).Port
                        ).ConfigureAwait(false);

                        void OnNotifyServerCert(Certificate certificate)
                        {
                            X509Certificate2[] serverCertChain = certificate.GetCertificateList()
                                .Select(x => new X509Certificate2(x.GetDerEncoded()))
                                .ToArray();

                            PrintCerts(serverCertChain);
                            Console.WriteLine();
                        }

                        using (TcpClient serverSocket = await serverSocketTask.ConfigureAwait(false))
                        {
                            using (var serverSslStream = new SslStream(serverSocket.GetStream()))
                            {
                                Task serverHandshakeTask = serverSslStream.AuthenticateAsServerAsync(
                                    endEntity,
                                    false,
                                    SslProtocols.Tls12,
                                    false
                                );

                                var tlsClientProto = new TlsClientProtocol(
                                    clientSocket.GetStream(),
                                    new SecureRandom()
                                );

                                tlsClientProto.Connect(new TlsClient(OnNotifyServerCert));
                                await serverHandshakeTask.ConfigureAwait(false);
                            }
                        }
                    }

                    Console.WriteLine("-------");

                    Thread.Sleep(TimeSpan.FromSeconds(30));
                }

                Console.WriteLine();
                Console.WriteLine("Building new chain explicitly...");
                Console.WriteLine();

                localChain = new X509Chain(true)
                {
                    ChainPolicy = new X509ChainPolicy
                    {
                        RevocationMode = X509RevocationMode.NoCheck,
                        VerificationTime = DateTime.Now,
                    },
                };
                localChain.Build(endEntity);
                PrintCertChain(localChain.ChainElements);
                Console.WriteLine();
            }
            finally
            {
                PurgeCertificates(oldRootCa, newRootCa, oldFirstIntermediate, secondIntermediate);
            }
        }

        private static async Task Main()
        {
            GenerateCerts(
                out X509Certificate2 oldRootCa,
                out X509Certificate2 newRootCa,
                out X509Certificate2 oldFirstIntermediate,
                out X509Certificate2 secondIntermediate,
                out X509Certificate2 endEntity
            );

            await TestSslStreamChainBuildingAsync(
                oldRootCa,
                newRootCa,
                oldFirstIntermediate,
                secondIntermediate,
                endEntity
            ).ConfigureAwait(false);
        }
    }
}

Note that the test application must be run as Administrator on Windows, because it installs (and afterwards removes) certificates into the trusted root CA store of the machine. Also, it depends on the external Portable.BouncyCastle NuGet package (I've used version 1.8.8), because there are no native .NET abstractions that can be used to log the certificates that are sent in a TLS Certificate message (HttpClient uses SslStream which will build a certificate chain from the certificates in the TLS message).

The test code produces the following output:

Testing chain locally...

0: CN=some.server.invalid, 27.10.2018 08:21:00 +00:00 - 27.10.2030 08:21:00 +00:00
1: CN=TEST_CERT_Second_Intermediate_CA, 27.10.2018 08:21:00 +00:00 - 27.10.2030 08:21:00 +00:00
2: CN=TEST_CERT_New_root_CA, 27.10.2010 08:21:00 +00:00 - 27.10.2020 08:23:00 +00:00
3: CN=TEST_CERT_Old_root_CA, 27.10.2010 08:21:00 +00:00 - 27.10.2020 08:23:00 +00:00

Starting test...

Chain #0, time is now: 27.10.2020 08:21:01 +00:00
0: CN=some.server.invalid, 27.10.2018 08:21:00 +00:00 - 27.10.2030 08:21:00 +00:00
1: CN=TEST_CERT_Second_Intermediate_CA, 27.10.2018 08:21:00 +00:00 - 27.10.2030 08:21:00 +00:00
2: CN=TEST_CERT_New_root_CA, 27.10.2010 08:21:00 +00:00 - 27.10.2020 08:23:00 +00:00

-------

Chain #1, time is now: 27.10.2020 08:21:31 +00:00
0: CN=some.server.invalid, 27.10.2018 08:21:00 +00:00 - 27.10.2030 08:21:00 +00:00
1: CN=TEST_CERT_Second_Intermediate_CA, 27.10.2018 08:21:00 +00:00 - 27.10.2030 08:21:00 +00:00
2: CN=TEST_CERT_New_root_CA, 27.10.2010 08:21:00 +00:00 - 27.10.2020 08:23:00 +00:00

-------

Chain #2, time is now: 27.10.2020 08:22:01 +00:00
0: CN=some.server.invalid, 27.10.2018 08:21:00 +00:00 - 27.10.2030 08:21:00 +00:00
1: CN=TEST_CERT_Second_Intermediate_CA, 27.10.2018 08:21:00 +00:00 - 27.10.2030 08:21:00 +00:00
2: CN=TEST_CERT_New_root_CA, 27.10.2010 08:21:00 +00:00 - 27.10.2020 08:23:00 +00:00

-------

Chain #3, time is now: 27.10.2020 08:22:31 +00:00
0: CN=some.server.invalid, 27.10.2018 08:21:00 +00:00 - 27.10.2030 08:21:00 +00:00
1: CN=TEST_CERT_Second_Intermediate_CA, 27.10.2018 08:21:00 +00:00 - 27.10.2030 08:21:00 +00:00
2: CN=TEST_CERT_New_root_CA, 27.10.2010 08:21:00 +00:00 - 27.10.2020 08:23:00 +00:00

-------

Chain #4, time is now: 27.10.2020 08:23:01 +00:00
0: CN=some.server.invalid, 27.10.2018 08:21:00 +00:00 - 27.10.2030 08:21:00 +00:00
1: CN=TEST_CERT_Second_Intermediate_CA, 27.10.2018 08:21:00 +00:00 - 27.10.2030 08:21:00 +00:00
2: CN=TEST_CERT_New_root_CA, 27.10.2010 08:21:00 +00:00 - 27.10.2020 08:23:00 +00:00

-------

Chain #5, time is now: 27.10.2020 08:23:31 +00:00
0: CN=some.server.invalid, 27.10.2018 08:21:00 +00:00 - 27.10.2030 08:21:00 +00:00
1: CN=TEST_CERT_Second_Intermediate_CA, 27.10.2018 08:21:00 +00:00 - 27.10.2030 08:21:00 +00:00
2: CN=TEST_CERT_New_root_CA, 27.10.2010 08:21:00 +00:00 - 27.10.2020 08:23:00 +00:00

-------

Chain #6, time is now: 27.10.2020 08:24:01 +00:00
0: CN=some.server.invalid, 27.10.2018 08:21:00 +00:00 - 27.10.2030 08:21:00 +00:00
1: CN=TEST_CERT_Second_Intermediate_CA, 27.10.2018 08:21:00 +00:00 - 27.10.2030 08:21:00 +00:00
2: CN=TEST_CERT_New_root_CA, 27.10.2010 08:21:00 +00:00 - 27.10.2020 08:23:00 +00:00

-------

Building new chain explicitly...

0: CN=some.server.invalid, 27.10.2018 08:21:00 +00:00 - 27.10.2030 08:21:00 +00:00
1: CN=TEST_CERT_Second_Intermediate_CA, 27.10.2018 08:21:00 +00:00 - 27.10.2030 08:21:00 +00:00
2: CN=TEST_CERT_New_root_CA, 27.10.2000 08:21:00 +00:00 - 27.10.2040 08:21:00 +00:00

As can be seen, the old root certificate (CN=TEST_CERT_Old_root_CA) and its first intermediate (CN=TEST_CERT_New_root_CA) both expire @ 27.10.2020 08:23:00 +00:00, yet the web client receives certificate chains containing these certificates even after they expire (chain iterations 4 - 6). After the certificates expire, building a new chain using X509Chain.Build() returns the expected chain, though.

Conclusion

By using the Visual Studio debugger with just-my-code debugging turned off, I have been able to set breakpoints in SecureChannel.cs and SslSessionsCache.cs, and verify that new SslStream objects are indeed reusing the same SCHANNEL_CRED handle for the same end-entity certificate due to the static credential cache. From the documentation of AcquireCredentialsHandle, it seems the certificate chain associated with the handle is built when AcquireCredentialsHandle is called, which in this case only happens the first time the SCHANNEL_CRED handle is created. Therefore, I believe this to be the culprit of the problem.

@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.

@vcsjones
Copy link
Member

/cc @wfurt

@wfurt
Copy link
Member

wfurt commented Oct 29, 2020

yes, I saw this @vcsjones. The cache is internal to SslStream. We would need to find expiration for entire chain and attache that to cached entries. However, do we know that the chain build with multiple roots would work?

The cache is there for ages and adds complexity. But I don't know how much it improves perf. It may be worth of measuring perf without it.

@egraff
Copy link
Author

egraff commented Nov 2, 2020

However, do we know that the chain build with multiple roots would work?

@wfurt If I understand your question correctly, this is part of what my test code shows.

Before the old CA root expires, X509Chain.Build() returns this chain:

Testing chain locally...

0: CN=some.server.invalid, 27.10.2018 08:21:00 +00:00 - 27.10.2030 08:21:00 +00:00
1: CN=TEST_CERT_Second_Intermediate_CA, 27.10.2018 08:21:00 +00:00 - 27.10.2030 08:21:00 +00:00
2: CN=TEST_CERT_New_root_CA, 27.10.2010 08:21:00 +00:00 - 27.10.2020 08:23:00 +00:00
3: CN=TEST_CERT_Old_root_CA, 27.10.2010 08:21:00 +00:00 - 27.10.2020 08:23:00 +00:00

and after it expired, it returns this chain:

Building new chain explicitly...

0: CN=some.server.invalid, 27.10.2018 08:21:00 +00:00 - 27.10.2030 08:21:00 +00:00
1: CN=TEST_CERT_Second_Intermediate_CA, 27.10.2018 08:21:00 +00:00 - 27.10.2030 08:21:00 +00:00
2: CN=TEST_CERT_New_root_CA, 27.10.2000 08:21:00 +00:00 - 27.10.2040 08:21:00 +00:00

If it's still the case in .NET Core that all of the chain building on Windows is left up to the CryptoAPI (i.e. CertGetCertificateChain), then I believe it should handle multiple roots fine. However, I've also opened #43884 for problems with handling multiple roots when using openssl as the crypto back-end.

@ghost
Copy link

ghost commented Jul 6, 2021

Tagging subscribers to this area: @dotnet/ncl, @vcsjones
See info in area-owners.md if you want to be subscribed.

Issue Details

Description

The static SslSessionsCache used by SecureChannel -- which in turn is used by SslStream -- to cache SCHANNEL_CRED handles is problematic, because the certificate chain for the end-entity certificate used by SslStream is only built when the SCHANNEL_CRED handle is first acquired, through a call to AcquireCredentialsHandle. If the root CA certificate or one of the intermediate CA certificates expire (this actually happened IRL quite recently), then all SslStream instances created for the rest of the entire lifetime of the process for the same end-entity certificate (for example a Kestrel-based ASP.Net Core web server) will continue to use the expired certificate chain.

Configuration

Seen on .NET on Windows. I have reproduced the problem on .NET Framework 4.8, .NET Core 2.1 and .NET Core 3.1.

Detailed reproduction

This problem was first observed in real life, when the Sectigo AddTrust External CA Root expired on May 30, 2020. A long-living web server serving the following certificate chain (end-entity certificate excluded) continued to serve the same, expired chain after the AddTrust CA certificate expired.

Subject Serial number
C=SE, O=AddTrust AB, OU=AddTrust External TTP Network, CN=AddTrust External CA Root
C=US, ST=New Jersey, L=Jersey City, O=The USERTRUST Network, CN=USERTrust RSA Certification Authority 13:ea:28:70:5b:f4:ec:ed:0c:36:63:09:80:61:43:36
C=GB, ST=Greater Manchester, L=Salford, O=Sectigo Limited, CN=Sectigo RSA Domain Validation Secure Server CA 7d:5b:51:26:b4:76:ba:11:db:74:16:0b:bc:53:0d:a7

The USERTrust intermediate certificate in this chain is a cross-signed CA, which also has a valid root CA certificate, so the expected certificate chain to be returned from the server after the AddTrust certificate expired would be this:

Subject Serial number
C=US, ST=New Jersey, L=Jersey City, O=The USERTRUST Network, CN=USERTrust RSA Certification Authority 01:fd:6d:30:fc:a3:ca:51:a8:1b:bc:64:0e:35:03:2d
C=GB, ST=Greater Manchester, L=Salford, O=Sectigo Limited, CN=Sectigo RSA Domain Validation Secure Server CA 7d:5b:51:26:b4:76:ba:11:db:74:16:0b:bc:53:0d:a7

I have reproduced this issue synthetically, with a stand-alone .NET executable. The application generates the following certificates:

    Old root CA (CN=TEST_CERT_Old_root_CA)
                   |
                   v
Old first intermediate (CN=TEST_CERT_New_root_CA)     New root CA (CN=TEST_CERT_New_root_CA)
               |                                            |
               v                                            v
           Second intermediate (CN=TEST_CERT_Second_Intermediate_CA)
                      |
                      v
      End entity (CN=some.server.invalid)

where Old first intermediate has the same subject name and private key as New root CA (i.e. the CA is cross-signed by Old root CA). Old root CA and Old first intermediate are set to expire in 2 minutes, but the notBefore date is set to be more recent than New root CA to trick CryptoAPI to prefer that certificate chain initially. After this is done, the root and intermediate CA certificates are installed in the system's certificate stores.

After the certificates are setup, the test first builds a certificate chain explicitly, using X509Chain.Build(). Then, the test runs a loop where it creates a new web server SslStream for each iteration, and logs the certificate chain that is received by a connecting web client (note that the root certificate is not included, as it is not part of the TLS Certificate message). There is a 30 seconds sleep between each loop iteration. Finally, after all the loop iterations have run, the test builds another certificate chain explicitly, again using X509Chain.Build().

Expand to see test code included below:
namespace SslStreamCertChainProblemExternalRepro
{
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Linq;
    using System.Net;
    using System.Net.Security;
    using System.Net.Sockets;
    using System.Security.Authentication;
    using System.Security.Cryptography;
    using System.Security.Cryptography.X509Certificates;
    using System.Threading;
    using System.Threading.Tasks;

    using Org.BouncyCastle.Crypto.Tls;
    using Org.BouncyCastle.Security;

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

    internal class TlsClient : DefaultTlsClient
    {
        private readonly Action<Certificate> onNotifyServerCert;

        /// <inheritdoc />
        public TlsClient(Action<Certificate> onNotifyServerCert)
        {
            this.onNotifyServerCert = onNotifyServerCert;
        }

        /// <inheritdoc />
        public override Org.BouncyCastle.Crypto.Tls.TlsAuthentication GetAuthentication()
        {
            return new TlsAuthentication(onNotifyServerCert);
        }

        private class TlsAuthentication : Org.BouncyCastle.Crypto.Tls.TlsAuthentication
        {
            private readonly Action<Certificate> onNotifyServerCert;

            public TlsAuthentication(Action<Certificate> onNotifyServerCert)
            {
                this.onNotifyServerCert = onNotifyServerCert;
            }

            /// <inheritdoc />
            public void NotifyServerCertificate(Certificate serverCertificate)
            {
                onNotifyServerCert(serverCertificate);
            }

            /// <inheritdoc />
            public TlsCredentials GetClientCredentials(
                Org.BouncyCastle.Crypto.Tls.CertificateRequest certificateRequest
            )
            {
                return null;
            }
        }
    }

    internal class Program
    {
        private static readonly RSAParameters OldRootCaRsaParams = 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 NewRootCaRsaParams = 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 OldRootCaName =
            new X500DistinguishedName("CN=TEST_CERT_Old_root_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 void PrintCerts(IEnumerable<X509Certificate2> certs)
        {
            foreach ((X509Certificate2 cert, int idx) in certs.Select((x, i) => (x, i)))
            {
                Console.WriteLine(
                    "{0}: {1}, {2} - {3}",
                    idx,
                    cert.Subject,
                    new DateTimeOffset(cert.NotBefore.ToUniversalTime()),
                    new DateTimeOffset(cert.NotAfter.ToUniversalTime())
                );
            }
        }

        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 oldRootCa,
            out X509Certificate2 newRootCa,
            out X509Certificate2 oldFirstIntermediate,
            out X509Certificate2 secondIntermediate,
            out X509Certificate2 endEntity
        )
        {
            DateTimeOffset now = GetUtcNowWithSecondResolution();

            RSA oldRootCaKey = RSA.Create(OldRootCaRsaParams);
            RSA newRootCaKey = RSA.Create(NewRootCaRsaParams);
            RSA secondIntermediateKey = RSA.Create(SecondIntermediateRsaParams);
            RSA endEntityKey = RSA.Create(EndEntityRsaParams);

            /*
             *         Old root CA
             *                 |
             *                 v
             *  Old first intermediate     New root CA
             *                 |             |
             *                 v             v
             *               Second intermediate
             *                        |
             *                        v
             *                   End entity  
             */

            oldRootCa = CreateCert(
                key: oldRootCaKey,
                subjectName: OldRootCaName,
                serialNumber: new byte[] { 0x01 },
                issuerKey: oldRootCaKey,
                issuerName: OldRootCaName,
                notBefore: now.AddYears(-10),
                notAfter: now.AddMinutes(2),
                isCa: true
            );

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

            oldFirstIntermediate = CreateCert(
                key: newRootCaKey,
                subjectName: NewRootCaName,
                serialNumber: new byte[] { 0x03 },
                issuerKey: oldRootCaKey,
                issuerName: OldRootCaName,
                notBefore: now.AddYears(-10),
                notAfter: now.AddMinutes(2),
                isCa: true
            );

            secondIntermediate = CreateCert(
                key: secondIntermediateKey,
                subjectName: SecondIntermediateCaName,
                serialNumber: new byte[] { 0x04 },
                issuerKey: newRootCaKey,
                issuerName: NewRootCaName,
                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 void PurgeCertificateFromStore(X509Store store, string subjectName)
        {
            X509Certificate2Collection found = store.Certificates.Find(
                X509FindType.FindBySubjectDistinguishedName,
                subjectName,
                validOnly: false
            );
            if (found.Count > 0)
            {
                store.RemoveRange(found);
            }
        }

        private static void InstallCertificates(
            X509Certificate2 oldRootCa,
            X509Certificate2 newRootCa,
            X509Certificate2 oldFirstIntermediate,
            X509Certificate2 secondIntermediate
        )
        {
            const StoreLocation storeLocation = StoreLocation.LocalMachine;

            using (var intermediateStore = new X509Store(
                StoreName.CertificateAuthority,
                storeLocation
            ))
            using (var rootCaStore = new X509Store(StoreName.Root, storeLocation))
            {
                intermediateStore.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadWrite);
                rootCaStore.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadWrite);

                intermediateStore.Add(oldFirstIntermediate);
                intermediateStore.Add(secondIntermediate);

                rootCaStore.Add(oldRootCa);
                rootCaStore.Add(newRootCa);
            }
        }

        private static void PurgeCertificates(
            X509Certificate2 oldRootCa,
            X509Certificate2 newRootCa,
            X509Certificate2 oldFirstIntermediate,
            X509Certificate2 secondIntermediate
        )
        {
            const StoreLocation storeLocation = StoreLocation.LocalMachine;

            using (var intermediateStore = new X509Store(
                StoreName.CertificateAuthority,
                storeLocation
            ))
            using (var rootCaStore = new X509Store(StoreName.Root, storeLocation))
            {
                intermediateStore.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadWrite);
                rootCaStore.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadWrite);

                PurgeCertificateFromStore(intermediateStore, oldFirstIntermediate.Subject);
                PurgeCertificateFromStore(intermediateStore, secondIntermediate.Subject);
                PurgeCertificateFromStore(rootCaStore, oldRootCa.Subject);
                PurgeCertificateFromStore(rootCaStore, newRootCa.Subject);
            }
        }

        private static async Task TestSslStreamChainBuildingAsync(
            X509Certificate2 oldRootCa,
            X509Certificate2 newRootCa,
            X509Certificate2 oldFirstIntermediate,
            X509Certificate2 secondIntermediate,
            X509Certificate2 endEntity
        )
        {
            try
            {
                PurgeCertificates(oldRootCa, newRootCa, oldFirstIntermediate, secondIntermediate);
                InstallCertificates(oldRootCa, newRootCa, oldFirstIntermediate, secondIntermediate);

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

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

                Console.WriteLine("Starting test...");

                TcpListener serverListenSocket = new TcpListener(IPAddress.Loopback, 0);
                serverListenSocket.Start();

                for (int loopCnt = 0; loopCnt < 7; loopCnt++)
                {
                    Console.WriteLine();
                    Console.WriteLine("Chain #{0}, time is now: {1}", loopCnt, DateTimeOffset.UtcNow);

                    Task<TcpClient> serverSocketTask = serverListenSocket.AcceptTcpClientAsync();
                    using (var clientSocket = new TcpClient())
                    {
                        await clientSocket.ConnectAsync(
                            IPAddress.Loopback,
                            ((IPEndPoint)serverListenSocket.LocalEndpoint).Port
                        ).ConfigureAwait(false);

                        void OnNotifyServerCert(Certificate certificate)
                        {
                            X509Certificate2[] serverCertChain = certificate.GetCertificateList()
                                .Select(x => new X509Certificate2(x.GetDerEncoded()))
                                .ToArray();

                            PrintCerts(serverCertChain);
                            Console.WriteLine();
                        }

                        using (TcpClient serverSocket = await serverSocketTask.ConfigureAwait(false))
                        {
                            using (var serverSslStream = new SslStream(serverSocket.GetStream()))
                            {
                                Task serverHandshakeTask = serverSslStream.AuthenticateAsServerAsync(
                                    endEntity,
                                    false,
                                    SslProtocols.Tls12,
                                    false
                                );

                                var tlsClientProto = new TlsClientProtocol(
                                    clientSocket.GetStream(),
                                    new SecureRandom()
                                );

                                tlsClientProto.Connect(new TlsClient(OnNotifyServerCert));
                                await serverHandshakeTask.ConfigureAwait(false);
                            }
                        }
                    }

                    Console.WriteLine("-------");

                    Thread.Sleep(TimeSpan.FromSeconds(30));
                }

                Console.WriteLine();
                Console.WriteLine("Building new chain explicitly...");
                Console.WriteLine();

                localChain = new X509Chain(true)
                {
                    ChainPolicy = new X509ChainPolicy
                    {
                        RevocationMode = X509RevocationMode.NoCheck,
                        VerificationTime = DateTime.Now,
                    },
                };
                localChain.Build(endEntity);
                PrintCertChain(localChain.ChainElements);
                Console.WriteLine();
            }
            finally
            {
                PurgeCertificates(oldRootCa, newRootCa, oldFirstIntermediate, secondIntermediate);
            }
        }

        private static async Task Main()
        {
            GenerateCerts(
                out X509Certificate2 oldRootCa,
                out X509Certificate2 newRootCa,
                out X509Certificate2 oldFirstIntermediate,
                out X509Certificate2 secondIntermediate,
                out X509Certificate2 endEntity
            );

            await TestSslStreamChainBuildingAsync(
                oldRootCa,
                newRootCa,
                oldFirstIntermediate,
                secondIntermediate,
                endEntity
            ).ConfigureAwait(false);
        }
    }
}

Note that the test application must be run as Administrator on Windows, because it installs (and afterwards removes) certificates into the trusted root CA store of the machine. Also, it depends on the external Portable.BouncyCastle NuGet package (I've used version 1.8.8), because there are no native .NET abstractions that can be used to log the certificates that are sent in a TLS Certificate message (HttpClient uses SslStream which will build a certificate chain from the certificates in the TLS message).

The test code produces the following output:

Testing chain locally...

0: CN=some.server.invalid, 27.10.2018 08:21:00 +00:00 - 27.10.2030 08:21:00 +00:00
1: CN=TEST_CERT_Second_Intermediate_CA, 27.10.2018 08:21:00 +00:00 - 27.10.2030 08:21:00 +00:00
2: CN=TEST_CERT_New_root_CA, 27.10.2010 08:21:00 +00:00 - 27.10.2020 08:23:00 +00:00
3: CN=TEST_CERT_Old_root_CA, 27.10.2010 08:21:00 +00:00 - 27.10.2020 08:23:00 +00:00

Starting test...

Chain #0, time is now: 27.10.2020 08:21:01 +00:00
0: CN=some.server.invalid, 27.10.2018 08:21:00 +00:00 - 27.10.2030 08:21:00 +00:00
1: CN=TEST_CERT_Second_Intermediate_CA, 27.10.2018 08:21:00 +00:00 - 27.10.2030 08:21:00 +00:00
2: CN=TEST_CERT_New_root_CA, 27.10.2010 08:21:00 +00:00 - 27.10.2020 08:23:00 +00:00

-------

Chain #1, time is now: 27.10.2020 08:21:31 +00:00
0: CN=some.server.invalid, 27.10.2018 08:21:00 +00:00 - 27.10.2030 08:21:00 +00:00
1: CN=TEST_CERT_Second_Intermediate_CA, 27.10.2018 08:21:00 +00:00 - 27.10.2030 08:21:00 +00:00
2: CN=TEST_CERT_New_root_CA, 27.10.2010 08:21:00 +00:00 - 27.10.2020 08:23:00 +00:00

-------

Chain #2, time is now: 27.10.2020 08:22:01 +00:00
0: CN=some.server.invalid, 27.10.2018 08:21:00 +00:00 - 27.10.2030 08:21:00 +00:00
1: CN=TEST_CERT_Second_Intermediate_CA, 27.10.2018 08:21:00 +00:00 - 27.10.2030 08:21:00 +00:00
2: CN=TEST_CERT_New_root_CA, 27.10.2010 08:21:00 +00:00 - 27.10.2020 08:23:00 +00:00

-------

Chain #3, time is now: 27.10.2020 08:22:31 +00:00
0: CN=some.server.invalid, 27.10.2018 08:21:00 +00:00 - 27.10.2030 08:21:00 +00:00
1: CN=TEST_CERT_Second_Intermediate_CA, 27.10.2018 08:21:00 +00:00 - 27.10.2030 08:21:00 +00:00
2: CN=TEST_CERT_New_root_CA, 27.10.2010 08:21:00 +00:00 - 27.10.2020 08:23:00 +00:00

-------

Chain #4, time is now: 27.10.2020 08:23:01 +00:00
0: CN=some.server.invalid, 27.10.2018 08:21:00 +00:00 - 27.10.2030 08:21:00 +00:00
1: CN=TEST_CERT_Second_Intermediate_CA, 27.10.2018 08:21:00 +00:00 - 27.10.2030 08:21:00 +00:00
2: CN=TEST_CERT_New_root_CA, 27.10.2010 08:21:00 +00:00 - 27.10.2020 08:23:00 +00:00

-------

Chain #5, time is now: 27.10.2020 08:23:31 +00:00
0: CN=some.server.invalid, 27.10.2018 08:21:00 +00:00 - 27.10.2030 08:21:00 +00:00
1: CN=TEST_CERT_Second_Intermediate_CA, 27.10.2018 08:21:00 +00:00 - 27.10.2030 08:21:00 +00:00
2: CN=TEST_CERT_New_root_CA, 27.10.2010 08:21:00 +00:00 - 27.10.2020 08:23:00 +00:00

-------

Chain #6, time is now: 27.10.2020 08:24:01 +00:00
0: CN=some.server.invalid, 27.10.2018 08:21:00 +00:00 - 27.10.2030 08:21:00 +00:00
1: CN=TEST_CERT_Second_Intermediate_CA, 27.10.2018 08:21:00 +00:00 - 27.10.2030 08:21:00 +00:00
2: CN=TEST_CERT_New_root_CA, 27.10.2010 08:21:00 +00:00 - 27.10.2020 08:23:00 +00:00

-------

Building new chain explicitly...

0: CN=some.server.invalid, 27.10.2018 08:21:00 +00:00 - 27.10.2030 08:21:00 +00:00
1: CN=TEST_CERT_Second_Intermediate_CA, 27.10.2018 08:21:00 +00:00 - 27.10.2030 08:21:00 +00:00
2: CN=TEST_CERT_New_root_CA, 27.10.2000 08:21:00 +00:00 - 27.10.2040 08:21:00 +00:00

As can be seen, the old root certificate (CN=TEST_CERT_Old_root_CA) and its first intermediate (CN=TEST_CERT_New_root_CA) both expire @ 27.10.2020 08:23:00 +00:00, yet the web client receives certificate chains containing these certificates even after they expire (chain iterations 4 - 6). After the certificates expire, building a new chain using X509Chain.Build() returns the expected chain, though.

Conclusion

By using the Visual Studio debugger with just-my-code debugging turned off, I have been able to set breakpoints in SecureChannel.cs and SslSessionsCache.cs, and verify that new SslStream objects are indeed reusing the same SCHANNEL_CRED handle for the same end-entity certificate due to the static credential cache. From the documentation of AcquireCredentialsHandle, it seems the certificate chain associated with the handle is built when AcquireCredentialsHandle is called, which in this case only happens the first time the SCHANNEL_CRED handle is created. Therefore, I believe this to be the culprit of the problem.

Author: egraff
Assignees: -
Labels:

area-System.Net.Security, untriaged

Milestone: -

@karelz karelz added this to the Future milestone Jul 13, 2021
@karelz karelz added bug and removed untriaged New issue has not been triaged by the area owner labels Jul 13, 2021
@rzikm rzikm self-assigned this Mar 7, 2022
@rzikm
Copy link
Member

rzikm commented Mar 8, 2022

This is the behavior I observe when I disable the credential caching (so that for each connection, new credentials are acquired)

❯ gsudo dotnet .\Sandbox.dll
Testing chain locally...

0: CN=some.server.invalid, 2020-03-08 11:58:02 +00:00 - 2032-03-08 11:58:02 +00:00
1: CN=TEST_CERT_Second_Intermediate_CA, 2020-03-08 11:58:02 +00:00 - 2032-03-08 11:58:02 +00:00
2: CN=TEST_CERT_New_root_CA, 2012-03-08 11:58:02 +00:00 - 2022-03-08 12:00:02 +00:00
3: CN=TEST_CERT_Old_root_CA, 2012-03-08 11:58:02 +00:00 - 2022-03-08 12:00:02 +00:00

Starting test...

Chain #0, time is now: 2022-03-08 11:58:05 +00:00
0: CN=some.server.invalid, 2020-03-08 11:58:02 +00:00 - 2032-03-08 11:58:02 +00:00
1: CN=TEST_CERT_Second_Intermediate_CA, 2020-03-08 11:58:02 +00:00 - 2032-03-08 11:58:02 +00:00
2: CN=TEST_CERT_New_root_CA, 2012-03-08 11:58:02 +00:00 - 2022-03-08 12:00:02 +00:00

-------

Chain #1, time is now: 2022-03-08 11:58:36 +00:00
0: CN=some.server.invalid, 2020-03-08 11:58:02 +00:00 - 2032-03-08 11:58:02 +00:00
1: CN=TEST_CERT_Second_Intermediate_CA, 2020-03-08 11:58:02 +00:00 - 2032-03-08 11:58:02 +00:00
2: CN=TEST_CERT_New_root_CA, 2012-03-08 11:58:02 +00:00 - 2022-03-08 12:00:02 +00:00

-------

Chain #2, time is now: 2022-03-08 11:59:06 +00:00
0: CN=some.server.invalid, 2020-03-08 11:58:02 +00:00 - 2032-03-08 11:58:02 +00:00
1: CN=TEST_CERT_Second_Intermediate_CA, 2020-03-08 11:58:02 +00:00 - 2032-03-08 11:58:02 +00:00
2: CN=TEST_CERT_New_root_CA, 2012-03-08 11:58:02 +00:00 - 2022-03-08 12:00:02 +00:00

-------

Chain #3, time is now: 2022-03-08 11:59:36 +00:00
0: CN=some.server.invalid, 2020-03-08 11:58:02 +00:00 - 2032-03-08 11:58:02 +00:00
1: CN=TEST_CERT_Second_Intermediate_CA, 2020-03-08 11:58:02 +00:00 - 2032-03-08 11:58:02 +00:00
2: CN=TEST_CERT_New_root_CA, 2012-03-08 11:58:02 +00:00 - 2022-03-08 12:00:02 +00:00

-------

Chain #4, time is now: 2022-03-08 12:00:06 +00:00
0: CN=some.server.invalid, 2020-03-08 11:58:02 +00:00 - 2032-03-08 11:58:02 +00:00
1: CN=TEST_CERT_Second_Intermediate_CA, 2020-03-08 11:58:02 +00:00 - 2032-03-08 11:58:02 +00:00

-------

Chain #5, time is now: 2022-03-08 12:00:36 +00:00
0: CN=some.server.invalid, 2020-03-08 11:58:02 +00:00 - 2032-03-08 11:58:02 +00:00
1: CN=TEST_CERT_Second_Intermediate_CA, 2020-03-08 11:58:02 +00:00 - 2032-03-08 11:58:02 +00:00

-------

Chain #6, time is now: 2022-03-08 12:01:06 +00:00
0: CN=some.server.invalid, 2020-03-08 11:58:02 +00:00 - 2032-03-08 11:58:02 +00:00
1: CN=TEST_CERT_Second_Intermediate_CA, 2020-03-08 11:58:02 +00:00 - 2032-03-08 11:58:02 +00:00

-------

Building new chain explicitly...

0: CN=some.server.invalid, 2020-03-08 11:58:02 +00:00 - 2032-03-08 11:58:02 +00:00
1: CN=TEST_CERT_Second_Intermediate_CA, 2020-03-08 11:58:02 +00:00 - 2032-03-08 11:58:02 +00:00
2: CN=TEST_CERT_New_root_CA, 2002-03-08 11:58:02 +00:00 - 2042-03-08 11:58:02 +00:00

So refreshing the creds certainly solves the problem.

Furthermore, I noticed that AcquireCredentialsHandle returns an expiry timestamp, which in this repro returned 2032-03-08 11:58:02. I tinkered with the dates a bit and it seems to me that it always corresponds to the NotAfter of the leaf certificate (maybe the implementation assumes that other certificates don't expire before due to validity period nesting?), so it does not really help us with this issue :/

Seems like that in order to fix this, we need to build the entire chain and manually find the earliest expiry date. I will try it and see how much of a perf hit that will be.

Edit: looks like we already build a cert chain and the result is conveniently accessible on SslAuthenticationOptions.CertificateContext. That looks promising.

@ghost ghost added the in-pr There is an active PR which will close this issue when it is merged label Mar 8, 2022
@ghost ghost removed the in-pr There is an active PR which will close this issue when it is merged label Mar 24, 2022
@karelz karelz modified the milestones: Future, 7.0.0 Apr 8, 2022
@ghost ghost locked as resolved and limited conversation to collaborators May 8, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants