-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
Comments
Tagging subscribers to this area: @bartonjs, @vcsjones, @krwq, @jeffhandley |
/cc @wfurt |
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. |
@wfurt If I understand your question correctly, this is part of what my test code shows. Before the old CA root expires,
and after it expired, it returns this chain:
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. |
Tagging subscribers to this area: @dotnet/ncl, @vcsjones Issue DetailsDescriptionThe static ConfigurationSeen on .NET on Windows. I have reproduced the problem on .NET Framework 4.8, .NET Core 2.1 and .NET Core 3.1. Detailed reproductionThis 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.
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:
I have reproduced this issue synthetically, with a stand-alone .NET executable. The application generates the following certificates:
where 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 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 ( The test code produces the following output:
As can be seen, the old root certificate ( ConclusionBy using the Visual Studio debugger with just-my-code debugging turned off, I have been able to set breakpoints in
|
This is the behavior I observe when I disable the credential caching (so that for each connection, new credentials are acquired)
So refreshing the creds certainly solves the problem. Furthermore, I noticed that 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 |
Description
The static
SslSessionsCache
used bySecureChannel
-- which in turn is used bySslStream
-- to cacheSCHANNEL_CRED
handles is problematic, because the certificate chain for the end-entity certificate used bySslStream
is only built when theSCHANNEL_CRED
handle is first acquired, through a call toAcquireCredentialsHandle
. If the root CA certificate or one of the intermediate CA certificates expire (this actually happened IRL quite recently), then allSslStream
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.
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:
I have reproduced this issue synthetically, with a stand-alone .NET executable. The application generates the following certificates:
where
Old first intermediate
has the same subject name and private key asNew root CA
(i.e. the CA is cross-signed byOld root CA
).Old root CA
andOld first intermediate
are set to expire in 2 minutes, but the notBefore date is set to be more recent thanNew 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:
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
usesSslStream
which will build a certificate chain from the certificates in the TLS message).The test code produces the following output:
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 usingX509Chain.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
andSslSessionsCache.cs
, and verify that newSslStream
objects are indeed reusing the sameSCHANNEL_CRED
handle for the same end-entity certificate due to the static credential cache. From the documentation ofAcquireCredentialsHandle
, it seems the certificate chain associated with the handle is built whenAcquireCredentialsHandle
is called, which in this case only happens the first time theSCHANNEL_CRED
handle is created. Therefore, I believe this to be the culprit of the problem.The text was updated successfully, but these errors were encountered: