Skip to content

Commit 1ea74bc

Browse files
adricastiaseigler
andauthored
Fix attestation certificate validation (#261)
* Fix attestation certificate validation The [attestationRootCertificates](https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-metadata-statement-v2.0-id-20180227.html#widl-MetadataStatement-attestationRootCertificates) published in FIDO Metadata Services contain an array of trust anchors that are used to validate device certificates. It could happen that these trust anchors are themselves issued by some other root certification authority. The .NET Framework X509Chain class correctly retrieves the untrusted root when building the chain, but the root is not published in MDS. This patch fixes this case. * Rework ValidateTrustChain() to catch all supported trust anchor configurations from FIDO MDS, add tests for same Co-authored-by: Alex Seigler <[email protected]>
1 parent 3a9bc86 commit 1ea74bc

File tree

2 files changed

+112
-16
lines changed

2 files changed

+112
-16
lines changed

Src/Fido2/CryptoUtils.cs

Lines changed: 64 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -64,28 +64,76 @@ public static HashAlgorithmName HashAlgFromCOSEAlg(COSE.Algorithm alg)
6464

6565
public static bool ValidateTrustChain(X509Certificate2[] trustPath, X509Certificate2[] attestationRootCertificates)
6666
{
67-
foreach (var attestationRootCert in attestationRootCertificates)
67+
// https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-metadata-statement-v2.0-id-20180227.html#widl-MetadataStatement-attestationRootCertificates
68+
69+
// Each element of this array represents a PKIX [RFC5280] X.509 certificate that is a valid trust anchor for this authenticator model.
70+
// Multiple certificates might be used for different batches of the same model.
71+
// The array does not represent a certificate chain, but only the trust anchor of that chain.
72+
// A trust anchor can be a root certificate, an intermediate CA certificate or even the attestation certificate itself.
73+
74+
// Let's check the simplest case first. If subject and issuer are the same, and the attestation cert is in the list, that's all the validation we need
75+
if (trustPath.Length == 1 && trustPath[0].Subject.CompareTo(trustPath[0].Issuer) == 0)
6876
{
69-
var chain = new X509Chain();
70-
chain.ChainPolicy.ExtraStore.Add(attestationRootCert);
71-
chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
72-
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
73-
if (trustPath.Length > 1)
77+
foreach (X509Certificate2? cert in attestationRootCertificates)
7478
{
75-
foreach (var cert in trustPath.Skip(1).Reverse())
76-
{
77-
chain.ChainPolicy.ExtraStore.Add(cert);
78-
}
79+
if (cert.Thumbprint.CompareTo(trustPath[0].Thumbprint) == 0)
80+
return true;
7981
}
80-
bool valid = chain.Build(trustPath[0]);
82+
return false;
83+
}
8184

82-
// because we are using AllowUnknownCertificateAuthority we have to verify that the root matches ourselves
83-
var chainRoot = chain.ChainElements[^1].Certificate;
84-
valid = valid && chainRoot.RawData.SequenceEqual(attestationRootCert.RawData);
85+
// If the attestation cert is not self signed, we will need to build a chain
86+
var chain = new X509Chain();
8587

86-
if (valid)
87-
return true;
88+
// Put all potential trust anchors into extra store
89+
chain.ChainPolicy.ExtraStore.AddRange(attestationRootCertificates);
90+
91+
// We don't know the root here, so allow unknown root, and turn off revocation checking
92+
chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
93+
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
94+
95+
// trustPath[0] is the attestation cert, if there are more in the array than just that, add those to the extra store as well, but skip attestation cert
96+
if (trustPath.Length > 1)
97+
{
98+
foreach (X509Certificate2? cert in trustPath.Skip(1)) // skip attestation cert
99+
{
100+
chain.ChainPolicy.ExtraStore.Add(cert);
101+
}
88102
}
103+
104+
// try to build a chain with what we've got
105+
if (chain.Build(trustPath[0]))
106+
{
107+
// if that validated, we should have a root for this chain now, add it to the custom trust store
108+
chain.ChainPolicy.CustomTrustStore.Clear();
109+
chain.ChainPolicy.CustomTrustStore.Add(chain.ChainElements[^1].Certificate);
110+
111+
// explicitly trust the custom root we just added
112+
chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
113+
114+
// if the attestation cert has a CDP extension, go ahead and turn on online revocation checking
115+
if (!string.IsNullOrEmpty(CDPFromCertificateExts(trustPath[0].Extensions)))
116+
chain.ChainPolicy.RevocationMode = X509RevocationMode.Online;
117+
118+
// don't allow unknown root now that we have a custom root
119+
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.NoFlag;
120+
121+
// now, verify chain again with all checks turned on
122+
if (chain.Build(trustPath[0]))
123+
{
124+
// if the chain validates, make sure one of the attestation root certificates is one of the chain elements
125+
foreach (X509Certificate2? attestationRootCertificate in attestationRootCertificates)
126+
{
127+
// skip the first element, as that is the attestation cert
128+
if (chain.ChainElements
129+
.Cast<X509ChainElement>()
130+
.Skip(1)
131+
.Any(x => x.Certificate.Thumbprint == attestationRootCertificate.Thumbprint))
132+
return true;
133+
}
134+
}
135+
}
136+
89137
return false;
90138
}
91139

Test/CryptoUtilsTests.cs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,53 @@ public void TestCertInCRLTrueCase()
3232

3333
Assert.True(CryptoUtils.IsCertInCRL(crl, cert));
3434
}
35+
36+
[Fact]
37+
public void TestValidateTrustChainRootAnchor()
38+
{
39+
var attestationRootCertificates = new X509Certificate2[3]
40+
{
41+
new X509Certificate2(Convert.FromBase64String("MIIBfjCCASWgAwIBAgIBATAKBggqhkjOPQQDAjAXMRUwEwYDVQQDDAxGVCBGSURPIDAyMDAwIBcNMTYwNTAxMDAwMDAwWhgPMjA1MDA1MDEwMDAwMDBaMBcxFTATBgNVBAMMDEZUIEZJRE8gMDIwMDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABNBmrRqVOxztTJVN19vtdqcL7tKQeol2nnM2/yYgvksZnr50SKbVgIEkzHQVOu80LVEE3lVheO1HjggxAlT6o4WjYDBeMB0GA1UdDgQWBBRJFWQt1bvG3jM6XgmV/IcjNtO/CzAfBgNVHSMEGDAWgBRJFWQt1bvG3jM6XgmV/IcjNtO/CzAMBgNVHRMEBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAgNHADBEAiAwfPqgIWIUB+QBBaVGsdHy0s5RMxlkzpSX/zSyTZmUpQIgB2wJ6nZRM8oX/nA43Rh6SJovM2XwCCH//+LirBAbB0M=")),
42+
new X509Certificate2(Convert.FromBase64String("MIIB2DCCAX6gAwIBAgIQFZ97ws2JGPEoa5NI+p8z1jAKBggqhkjOPQQDAjBLMQswCQYDVQQGEwJDTjEdMBsGA1UECgwURmVpdGlhbiBUZWNobm9sb2dpZXMxHTAbBgNVBAMMFEZlaXRpYW4gRklETyBSb290IENBMCAXDTE4MDQwMTAwMDAwMFoYDzIwNDgwMzMxMjM1OTU5WjBLMQswCQYDVQQGEwJDTjEdMBsGA1UECgwURmVpdGlhbiBUZWNobm9sb2dpZXMxHTAbBgNVBAMMFEZlaXRpYW4gRklETyBSb290IENBMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEnfAKbjvMX1Ey1b6k+WQQdNVMt9JgGWyJ3PvM4BSK5XqTfo++0oAj/4tnwyIL0HFBR9St+ktjqSXDfjiXAurs86NCMEAwHQYDVR0OBBYEFNGhmE2Bf8O5a/YHZ71QEv6QRfFUMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMCA0gAMEUCIQC3sT1lBjGeF+xKTpzV1KYU2ckahTd4mLJyzYOhaHv4igIgD2JYkfyH5Q4Bpo8rroO0It7oYjF2kgy/eSZ3U9Glaqw=")),
43+
new X509Certificate2(Convert.FromBase64String("MIIB2DCCAX6gAwIBAgIQGBUrQbdDrm20FZnDsX2CBTAKBggqhkjOPQQDAjBLMQswCQYDVQQGEwJVUzEdMBsGA1UECgwURmVpdGlhbiBUZWNobm9sb2dpZXMxHTAbBgNVBAMMFEZlaXRpYW4gRklETyBSb290IENBMCAXDTE4MDQwMTAwMDAwMFoYDzIwNDgwMzMxMjM1OTU5WjBLMQswCQYDVQQGEwJVUzEdMBsGA1UECgwURmVpdGlhbiBUZWNobm9sb2dpZXMxHTAbBgNVBAMMFEZlaXRpYW4gRklETyBSb290IENBMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEsFYEEhiJuqqnMgQjSiivBjV7DGCTf4XBBH/B7uvZsKxXShF0L8uDISWUvcExixRs6gB3oldSrjox6L8T94NOzqNCMEAwHQYDVR0OBBYEFEu9hyYRrRyJzwRYvnDSCIxrFiO3MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMAoGCCqGSM49BAMCA0gAMEUCIDHSb2mbNDAUNXvpPU0oWKeNye0fQ2l9D01AR2+sLZdhAiEAo3wz684IFMVsCCRmuJqxH6FQRESNqezuo1E+KkGxWuM="))
44+
};
45+
46+
var trustPath = new X509Certificate2[2]
47+
{
48+
new X509Certificate2(Convert.FromBase64String("MIICQzCCAemgAwIBAgIQHfK1WlHcS2iFo9meaX/tFjAKBggqhkjOPQQDAjBJMQswCQYDVQQGEwJVUzEdMBsGA1UECgwURmVpdGlhbiBUZWNobm9sb2dpZXMxGzAZBgNVBAMMEkZlaXRpYW4gRklETyBDQSAwMzAgFw0xODEyMjUwMDAwMDBaGA8yMDMzMTIyNDIzNTk1OVowcDELMAkGA1UEBhMCVVMxHTAbBgNVBAoMFEZlaXRpYW4gVGVjaG5vbG9naWVzMSIwIAYDVQQLDBlBdXRoZW50aWNhdG9yIEF0dGVzdGF0aW9uMR4wHAYDVQQDDBVGVCBCaW9QYXNzIEZJRE8yIDA0NzAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAS62hIbyenH9WPnzYHehaBR3C7qswomZkaPzGyUlFRiJIMo3uITeImFOFfNcDuOzoq1wcXXGTmEtEtxF2wo9noko4GJMIGGMB0GA1UdDgQWBBSBI1XoLDY1/HJaba+W32nxhxp3WjAfBgNVHSMEGDAWgBRBt/xNdcqO0p8s0xebzYNRinnYqTAMBgNVHRMBAf8EAjAAMBMGCysGAQQBguUcAgEBBAQDAgRwMCEGCysGAQQBguUcAQEEBBIEEBLe10VL7UfUq6rnE/UdY5MwCgYIKoZIzj0EAwIDSAAwRQIhAI6GSVi10r673uqtso+2oB6f5S5gE0ff44t3NcQ+TN9NAiAC/SCP+eKw1BnmcSgbxcQpYuWjBPMVDfqeg8pbmOdHKw==")),
49+
new X509Certificate2(Convert.FromBase64String("MIIB+TCCAaCgAwIBAgIQGBUrQbdDrm20FZnDsX2CCDAKBggqhkjOPQQDAjBLMQswCQYDVQQGEwJVUzEdMBsGA1UECgwURmVpdGlhbiBUZWNobm9sb2dpZXMxHTAbBgNVBAMMFEZlaXRpYW4gRklETyBSb290IENBMCAXDTE4MDUyMDAwMDAwMFoYDzIwMzgwNTE5MjM1OTU5WjBJMQswCQYDVQQGEwJVUzEdMBsGA1UECgwURmVpdGlhbiBUZWNobm9sb2dpZXMxGzAZBgNVBAMMEkZlaXRpYW4gRklETyBDQSAwMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJts1KYQuj66rAszKKLfsOay91gO11vSvfcYd/dQfeTjpSNb55ffoLijQbRXspqE5Uj2NVylED61pjo2tpytOfijZjBkMB0GA1UdDgQWBBRBt/xNdcqO0p8s0xebzYNRinnYqTAfBgNVHSMEGDAWgBRLvYcmEa0cic8EWL5w0giMaxYjtzASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAgNHADBEAiAnSuhaqHgV3Sds/OrwiqLNUWMmU8Lji9Vy7s5hSEg22AIgE1lIdBjq0N+QcZq995uOE4XWxBIrVUio3RAwgDn8KgI="))
50+
};
51+
Assert.True(CryptoUtils.ValidateTrustChain(trustPath, attestationRootCertificates));
52+
Assert.False(CryptoUtils.ValidateTrustChain(trustPath, trustPath));
53+
Assert.False(CryptoUtils.ValidateTrustChain(attestationRootCertificates, attestationRootCertificates));
54+
Assert.False(CryptoUtils.ValidateTrustChain(attestationRootCertificates, trustPath));
55+
}
56+
[Fact]
57+
public void TestValidateTrustChainSubAnchor()
58+
{
59+
byte[] attRootCertBytes = Convert.FromBase64String("MIIDCDCCAq+gAwIBAgIQQAFqUNTHZ8kBN8u/bCk+xDAKBggqhkjOPQQDAjBrMQswCQYDVQQGEwJVUzETMBEGA1UEChMKSElEIEdsb2JhbDEiMCAGA1UECxMZQXV0aGVudGljYXRvciBBdHRlc3RhdGlvbjEjMCEGA1UEAxMaRklETyBBdHRlc3RhdGlvbiBSb290IENBIDEwHhcNMTkwNDI0MTkzMTIzWhcNNDQwNDI3MTkzMTIzWjBmMQswCQYDVQQGEwJVUzETMBEGA1UEChMKSElEIEdsb2JhbDEiMCAGA1UECxMZQXV0aGVudGljYXRvciBBdHRlc3RhdGlvbjEeMBwGA1UEAxMVRklETyBBdHRlc3RhdGlvbiBDQSAyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE4nK9ctzk6GEGFNQBcrnBBmWU+dCnuHQAARrB2Eyc8MbsljkSFhZtfz/Rw6SuVIDk5VakDzrKBAOJ9v0Rvg/406OCATgwggE0MBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgGGMIGEBggrBgEFBQcBAQR4MHYwLgYIKwYBBQUHMAGGImh0dHA6Ly9oaWQuZmlkby5vY3NwLmlkZW50cnVzdC5jb20wRAYIKwYBBQUHMAKGOGh0dHA6Ly92YWxpZGF0aW9uLmlkZW50cnVzdC5jb20vcm9vdHMvSElERklET1Jvb3RjYTEucDdjMB8GA1UdIwQYMBaAFB2m3iwWSYHvWTHbJiHAyKDp+CSjMEcGA1UdHwRAMD4wPKA6oDiGNmh0dHA6Ly92YWxpZGF0aW9uLmlkZW50cnVzdC5jb20vY3JsL0hJREZJRE9Sb290Y2ExLmNybDAdBgNVHQ4EFgQUDLCbuLslcclrOZIz57Fu0imSMQ8wCgYIKoZIzj0EAwIDRwAwRAIgDCW5IrbjEI/y35lPjx9a+/sF4lPSoZdBHgFgTWC+8VICIEqs2SPzUHgHVh65Ajl1oIUmhh0C2lyR/Zdk7O3u1TIK");
60+
var attestationRootCertificates = new X509Certificate2[1] { new X509Certificate2(attRootCertBytes) };
61+
62+
byte[] attCert = Convert.FromBase64String("MIIDLjCCAtSgAwIBAgIQQAFs2JXwQcL5Eh4rnp2ASjAKBggqhkjOPQQDAjBmMQswCQYDVQQGEwJVUzETMBEGA1UEChMKSElEIEdsb2JhbDEiMCAGA1UECxMZQXV0aGVudGljYXRvciBBdHRlc3RhdGlvbjEeMBwGA1UEAxMVRklETyBBdHRlc3RhdGlvbiBDQSAyMB4XDTE5MDgyODE0MTY0MFoXDTM5MDgyMzE0MTY0MFowaTELMAkGA1UEBhMCVVMxHzAdBgNVBAoTFkhJRCBHbG9iYWwgQ29ycG9yYXRpb24xIjAgBgNVBAsTGUF1dGhlbnRpY2F0b3IgQXR0ZXN0YXRpb24xFTATBgNVBAMTDENyZXNjZW5kb0tleTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABAGouI654w6qbGonSTStO2cESYTo8Ezr8OJiPkMl02d6K6i44wXCKV2i+w+bpR6vgYQZ/cKQxMS4uGytqPRNPIejggFfMIIBWzAOBgNVHQ8BAf8EBAMCB4AwgYAGCCsGAQUFBwEBBHQwcjAuBggrBgEFBQcwAYYiaHR0cDovL2hpZC5maWRvLm9jc3AuaWRlbnRydXN0LmNvbTBABggrBgEFBQcwAoY0aHR0cDovL3ZhbGlkYXRpb24uaWRlbnRydXN0LmNvbS9jZXJ0cy9oaWRmaWRvY2EyLnA3YzAfBgNVHSMEGDAWgBQMsJu4uyVxyWs5kjPnsW7SKZIxDzAJBgNVHRMEAjAAMEMGA1UdHwQ8MDowOKA2oDSGMmh0dHA6Ly92YWxpZGF0aW9uLmlkZW50cnVzdC5jb20vY3JsL2hpZGZpZG9jYTIuY3JsMBMGCysGAQQBguUcAgEBBAQDAgQwMB0GA1UdDgQWBBR9h/lCWeTiMUhRS1tj31hBXaOurzAhBgsrBgEEAYLlHAEBBAQSBBBpLbVJeuVE1aHl3SCkk7cjMAoGCCqGSM49BAMCA0gAMEUCIQDpDa1ZbAfCTlBMiDUuB5XH8hnhZUF1JCuCmc+ShI4ZTwIga/ApAudL5R8HxOOHgk8AA/JpgCkMmYDQLVq0QF6oxrU=");
63+
var trustPath = new X509Certificate2[1] { new X509Certificate2(attCert) };
64+
65+
Assert.False(0 == attestationRootCertificates[0].Issuer.CompareTo(attestationRootCertificates[0].Subject));
66+
Assert.True(CryptoUtils.ValidateTrustChain(trustPath, attestationRootCertificates));
67+
Assert.False(CryptoUtils.ValidateTrustChain(trustPath, trustPath));
68+
Assert.False(CryptoUtils.ValidateTrustChain(attestationRootCertificates, attestationRootCertificates));
69+
Assert.False(CryptoUtils.ValidateTrustChain(attestationRootCertificates, trustPath));
70+
}
71+
[Fact]
72+
public void TestValidateTrustChainSelf()
73+
{
74+
byte[] certBytes = Convert.FromBase64String("MIIBzTCCAXOgAwIBAgIJALS3SibGDXTPMAoGCCqGSM49BAMCMDsxIDAeBgNVBAMMF0dvVHJ1c3QgRklETzIgUm9vdCBDQSAxMRcwFQYDVQQKDA5Hb1RydXN0SUQgSW5jLjAeFw0xOTEyMDQwNjU5NDBaFw00OTExMjYwNjU5NDBaMDsxIDAeBgNVBAMMF0dvVHJ1c3QgRklETzIgUm9vdCBDQSAxMRcwFQYDVQQKDA5Hb1RydXN0SUQgSW5jLjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABA5mjYsjowAI0jnpi//CJ3KnzhGbTUmstNWqN78ioG1CTK9gPgPl9UiFOJO/v+FfFK+Pxv10c604dvlIDAbKw+ijYDBeMAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSgWtY0nEcmPmGDLuCwceKeJPScozAfBgNVHSMEGDAWgBSgWtY0nEcmPmGDLuCwceKeJPScozAKBggqhkjOPQQDAgNIADBFAiAxoVs6qj7DX2xixCjjcDUdxBTJmSTLb0f1rRGwrABzTQIhAPt0P32qzAeepF4//tgzxqNoKkWDcaPPSXrg+xzrlVHw");
75+
var certs = new X509Certificate2[1] { new X509Certificate2(certBytes) };
76+
77+
byte[] otherCertBytes = Convert.FromBase64String("MIIDRjCCAu2gAwIBAgIUZPhSDtxI5lg2qgy+7IGDJhGqPOgwCgYIKoZIzj0EAwIwgYcxCzAJBgNVBAYTAlRXMQ8wDQYDVQQIDAZUYWlwZWkxEjAQBgNVBAcMCVNvbWV3aGVyZTEWMBQGA1UECgwNV2lTRUNVUkUgSW5jLjEgMB4GCSqGSIb3DQEJARYRYWRtaW5AZXhhbXBsZS5vcmcxGTAXBgNVBAMMEFdpU0VDVVJFIFJvb3QgQ0EwHhcNMjEwMTI4MDgyNzIwWhcNMzEwMTI2MDgyNzIwWjCBhzELMAkGA1UEBhMCVFcxDzANBgNVBAgMBlRhaXBlaTESMBAGA1UEBwwJU29tZXdoZXJlMRYwFAYDVQQKDA1XaVNFQ1VSRSBJbmMuMSAwHgYJKoZIhvcNAQkBFhFhZG1pbkBleGFtcGxlLm9yZzEZMBcGA1UEAwwQV2lTRUNVUkUgUm9vdCBDQTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABBiWvFaf/IhFMOWNqlweqr4GfO0mu/1B18J03OG+pSltRix9GjRojBya4LARyXMP8nw2Xh9PvwOBm9QedMC66XGjggEzMIIBLzAdBgNVHQ4EFgQUd+Yvj6I3Y8cKH3QRNLlC8/Op97cwgccGA1UdIwSBvzCBvIAUd+Yvj6I3Y8cKH3QRNLlC8/Op97ehgY2kgYowgYcxCzAJBgNVBAYTAlRXMQ8wDQYDVQQIDAZUYWlwZWkxEjAQBgNVBAcMCVNvbWV3aGVyZTEWMBQGA1UECgwNV2lTRUNVUkUgSW5jLjEgMB4GCSqGSIb3DQEJARYRYWRtaW5AZXhhbXBsZS5vcmcxGTAXBgNVBAMMEFdpU0VDVVJFIFJvb3QgQ0GCFGT4Ug7cSOZYNqoMvuyBgyYRqjzoMAwGA1UdEwEB/wQCMAAwNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL3d3dy5leGFtcGxlLm9yZy9leGFtcGxlX2NhLmNybDAKBggqhkjOPQQDAgNHADBEAiBf3p8LJ3PlfMsxTzWgjHaal6uzIo5tx3o+EUybdDY4ogIgV6nR1MUE1wKz1uC7/kENg/FpJOetFaJePcgoneEwsKA=");
78+
var otherCerts = new X509Certificate2[1] { new X509Certificate2(otherCertBytes) };
79+
80+
Assert.True(CryptoUtils.ValidateTrustChain(certs, certs));
81+
Assert.False(CryptoUtils.ValidateTrustChain(certs, otherCerts));
82+
}
3583
}
3684
}

0 commit comments

Comments
 (0)