1// Copyright 2016 Google Inc. All Rights Reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package ctfe 16 17import ( 18 "encoding/base64" 19 "encoding/pem" 20 "testing" 21 "time" 22 23 "github.com/google/certificate-transparency-go/trillian/ctfe/testonly" 24 "github.com/google/certificate-transparency-go/x509" 25 "github.com/google/certificate-transparency-go/x509/pkix" 26 "github.com/google/certificate-transparency-go/x509util" 27) 28 29func wipeExtensions(cert *x509.Certificate) *x509.Certificate { 30 cert.Extensions = cert.Extensions[:0] 31 return cert 32} 33 34func makePoisonNonCritical(cert *x509.Certificate) *x509.Certificate { 35 // Invalid as a pre-cert because poison extension needs to be marked as critical. 36 cert.Extensions = []pkix.Extension{{Id: ctPoisonExtensionOID, Critical: false, Value: asn1NullBytes}} 37 return cert 38} 39 40func makePoisonNonNull(cert *x509.Certificate) *x509.Certificate { 41 // Invalid as a pre-cert because poison extension is not ASN.1 NULL value. 42 cert.Extensions = []pkix.Extension{{Id: ctPoisonExtensionOID, Critical: false, Value: []byte{0x42, 0x42, 0x42}}} 43 return cert 44} 45 46func TestIsPrecertificate(t *testing.T) { 47 var tests = []struct { 48 desc string 49 cert *x509.Certificate 50 wantPrecert bool 51 wantErr bool 52 }{ 53 { 54 desc: "valid-precert", 55 cert: pemToCert(t, testonly.PrecertPEMValid), 56 wantPrecert: true, 57 }, 58 { 59 desc: "valid-cert", 60 cert: pemToCert(t, testonly.CACertPEM), 61 wantPrecert: false, 62 }, 63 { 64 desc: "remove-exts-from-precert", 65 cert: wipeExtensions(pemToCert(t, testonly.PrecertPEMValid)), 66 wantPrecert: false, 67 }, 68 { 69 desc: "poison-non-critical", 70 cert: makePoisonNonCritical(pemToCert(t, testonly.PrecertPEMValid)), 71 wantPrecert: false, 72 wantErr: true, 73 }, 74 { 75 desc: "poison-non-null", 76 cert: makePoisonNonNull(pemToCert(t, testonly.PrecertPEMValid)), 77 wantPrecert: false, 78 wantErr: true, 79 }, 80 } 81 82 for _, test := range tests { 83 gotPrecert, err := IsPrecertificate(test.cert) 84 t.Run(test.desc, func(t *testing.T) { 85 if err != nil { 86 if !test.wantErr { 87 t.Errorf("IsPrecertificate()=%v,%v; want %v,nil", gotPrecert, err, test.wantPrecert) 88 } 89 return 90 } 91 if test.wantErr { 92 t.Errorf("IsPrecertificate()=%v,%v; want _,%v", gotPrecert, err, test.wantErr) 93 } 94 if gotPrecert != test.wantPrecert { 95 t.Errorf("IsPrecertificate()=%v,%v; want %v,nil", gotPrecert, err, test.wantPrecert) 96 } 97 }) 98 } 99} 100 101func TestValidateChain(t *testing.T) { 102 fakeCARoots := NewPEMCertPool() 103 if !fakeCARoots.AppendCertsFromPEM([]byte(testonly.FakeCACertPEM)) { 104 t.Fatal("failed to load fake root") 105 } 106 if !fakeCARoots.AppendCertsFromPEM([]byte(testonly.FakeRootCACertPEM)) { 107 t.Fatal("failed to load fake root") 108 } 109 validateOpts := CertValidationOpts{ 110 trustedRoots: fakeCARoots, 111 extKeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny}, 112 } 113 114 var tests = []struct { 115 desc string 116 chain [][]byte 117 wantErr bool 118 wantPathLen int 119 }{ 120 { 121 desc: "missing-intermediate-cert", 122 chain: pemsToDERChain(t, []string{testonly.LeafSignedByFakeIntermediateCertPEM}), 123 wantErr: true, 124 }, 125 { 126 desc: "wrong-cert-order", 127 chain: pemsToDERChain(t, []string{testonly.FakeIntermediateCertPEM, testonly.LeafSignedByFakeIntermediateCertPEM}), 128 wantErr: true, 129 }, 130 { 131 desc: "unrelated-cert-in-chain", 132 chain: pemsToDERChain(t, []string{testonly.FakeIntermediateCertPEM, testonly.TestCertPEM}), 133 wantErr: true, 134 }, 135 { 136 desc: "unrelated-cert-after-chain", 137 chain: pemsToDERChain(t, []string{testonly.LeafSignedByFakeIntermediateCertPEM, testonly.FakeIntermediateCertPEM, testonly.TestCertPEM}), 138 wantErr: true, 139 }, 140 { 141 desc: "valid-chain", 142 chain: pemsToDERChain(t, []string{testonly.LeafSignedByFakeIntermediateCertPEM, testonly.FakeIntermediateCertPEM}), 143 wantPathLen: 3, 144 }, 145 { 146 desc: "valid-chain-with-policyconstraints", 147 chain: pemsToDERChain(t, []string{testonly.LeafCertPEM, testonly.FakeIntermediateWithPolicyConstraintsCertPEM}), 148 wantPathLen: 3, 149 }, 150 { 151 desc: "valid-chain-with-policyconstraints-inc-root", 152 chain: pemsToDERChain(t, []string{testonly.LeafCertPEM, testonly.FakeIntermediateWithPolicyConstraintsCertPEM, testonly.FakeRootCACertPEM}), 153 wantPathLen: 3, 154 }, 155 { 156 desc: "valid-chain-with-nameconstraints", 157 chain: pemsToDERChain(t, []string{testonly.LeafCertPEM, testonly.FakeIntermediateWithNameConstraintsCertPEM}), 158 wantPathLen: 3, 159 }, 160 { 161 desc: "chain-with-invalid-nameconstraints", 162 chain: pemsToDERChain(t, []string{testonly.LeafCertPEM, testonly.FakeIntermediateWithInvalidNameConstraintsCertPEM}), 163 wantPathLen: 3, 164 }, 165 { 166 desc: "chain-of-len-4", 167 chain: pemFileToDERChain(t, "../testdata/subleaf.chain"), 168 wantPathLen: 4, 169 }, 170 { 171 desc: "misordered-chain-of-len-4", 172 chain: pemFileToDERChain(t, "../testdata/subleaf.misordered.chain"), 173 wantErr: true, 174 }, 175 } 176 for _, test := range tests { 177 t.Run(test.desc, func(t *testing.T) { 178 gotPath, err := ValidateChain(test.chain, validateOpts) 179 if err != nil { 180 if !test.wantErr { 181 t.Errorf("ValidateChain()=%v,%v; want _,nil", gotPath, err) 182 } 183 return 184 } 185 if test.wantErr { 186 t.Errorf("ValidateChain()=%v,%v; want _,non-nil", gotPath, err) 187 return 188 } 189 if len(gotPath) != test.wantPathLen { 190 t.Errorf("|ValidateChain()|=%d; want %d", len(gotPath), test.wantPathLen) 191 for _, c := range gotPath { 192 t.Logf("Subject: %s Issuer: %s", x509util.NameToString(c.Subject), x509util.NameToString(c.Issuer)) 193 } 194 } 195 }) 196 } 197} 198 199func TestCA(t *testing.T) { 200 fakeCARoots := NewPEMCertPool() 201 if !fakeCARoots.AppendCertsFromPEM([]byte(testonly.FakeCACertPEM)) { 202 t.Fatal("failed to load fake root") 203 } 204 validateOpts := CertValidationOpts{ 205 trustedRoots: fakeCARoots, 206 extKeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny}, 207 } 208 chain := pemsToDERChain(t, []string{testonly.LeafSignedByFakeIntermediateCertPEM, testonly.FakeIntermediateCertPEM}) 209 leaf, err := x509.ParseCertificate(chain[0]) 210 if x509.IsFatal(err) { 211 t.Fatalf("Failed to parse golden certificate DER: %v", err) 212 } 213 t.Logf("Cert expiry date: %v", leaf.NotAfter) 214 215 var tests = []struct { 216 desc string 217 chain [][]byte 218 caOnly bool 219 wantErr bool 220 }{ 221 { 222 desc: "end-entity, allow non-CA", 223 chain: chain, 224 }, 225 { 226 desc: "end-entity, disallow non-CA", 227 chain: chain, 228 caOnly: true, 229 wantErr: true, 230 }, 231 { 232 desc: "intermediate, allow non-CA", 233 chain: chain[1:], 234 }, 235 { 236 desc: "intermediate, disallow non-CA", 237 chain: chain[1:], 238 caOnly: true, 239 }, 240 } 241 for _, test := range tests { 242 t.Run(test.desc, func(t *testing.T) { 243 validateOpts.acceptOnlyCA = test.caOnly 244 gotPath, err := ValidateChain(test.chain, validateOpts) 245 if err != nil { 246 if !test.wantErr { 247 t.Errorf("ValidateChain()=%v,%v; want _,nil", gotPath, err) 248 } 249 return 250 } 251 if test.wantErr { 252 t.Errorf("ValidateChain()=%v,%v; want _,non-nil", gotPath, err) 253 } 254 }) 255 } 256} 257 258func TestNotAfterRange(t *testing.T) { 259 fakeCARoots := NewPEMCertPool() 260 if !fakeCARoots.AppendCertsFromPEM([]byte(testonly.FakeCACertPEM)) { 261 t.Fatal("failed to load fake root") 262 } 263 validateOpts := CertValidationOpts{ 264 trustedRoots: fakeCARoots, 265 rejectExpired: false, 266 extKeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny}, 267 } 268 269 chain := pemsToDERChain(t, []string{testonly.LeafSignedByFakeIntermediateCertPEM, testonly.FakeIntermediateCertPEM}) 270 271 var tests = []struct { 272 desc string 273 chain [][]byte 274 notAfterStart time.Time 275 notAfterLimit time.Time 276 wantErr bool 277 }{ 278 { 279 desc: "valid-chain, no range", 280 chain: chain, 281 }, 282 { 283 desc: "valid-chain, valid range", 284 chain: chain, 285 notAfterStart: time.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC), 286 notAfterLimit: time.Date(2020, 7, 1, 0, 0, 0, 0, time.UTC), 287 }, 288 { 289 desc: "before valid range", 290 chain: chain, 291 notAfterStart: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC), 292 wantErr: true, 293 }, 294 { 295 desc: "after valid range", 296 chain: chain, 297 notAfterLimit: time.Date(1999, 1, 1, 0, 0, 0, 0, time.UTC), 298 wantErr: true, 299 }, 300 } 301 for _, test := range tests { 302 t.Run(test.desc, func(t *testing.T) { 303 if !test.notAfterStart.IsZero() { 304 validateOpts.notAfterStart = &test.notAfterStart 305 } 306 if !test.notAfterLimit.IsZero() { 307 validateOpts.notAfterLimit = &test.notAfterLimit 308 } 309 gotPath, err := ValidateChain(test.chain, validateOpts) 310 if err != nil { 311 if !test.wantErr { 312 t.Errorf("ValidateChain()=%v,%v; want _,nil", gotPath, err) 313 } 314 return 315 } 316 if test.wantErr { 317 t.Errorf("ValidateChain()=%v,%v; want _,non-nil", gotPath, err) 318 } 319 }) 320 } 321} 322 323// Builds a chain of DER-encoded certs. 324// Note: ordering is important 325func pemsToDERChain(t *testing.T, pemCerts []string) [][]byte { 326 t.Helper() 327 chain := make([][]byte, 0, len(pemCerts)) 328 for _, pemCert := range pemCerts { 329 cert := pemToCert(t, pemCert) 330 chain = append(chain, cert.Raw) 331 } 332 return chain 333} 334 335func pemToCert(t *testing.T, pemData string) *x509.Certificate { 336 t.Helper() 337 bytes, rest := pem.Decode([]byte(pemData)) 338 if len(rest) > 0 { 339 t.Fatalf("Extra data after PEM: %v", rest) 340 return nil 341 } 342 343 cert, err := x509.ParseCertificate(bytes.Bytes) 344 if x509.IsFatal(err) { 345 t.Fatal(err) 346 } 347 348 return cert 349} 350 351func pemFileToDERChain(t *testing.T, filename string) [][]byte { 352 t.Helper() 353 rawChain, err := x509util.ReadPossiblePEMFile(filename, "CERTIFICATE") 354 if err != nil { 355 t.Fatalf("failed to load testdata: %v", err) 356 } 357 return rawChain 358} 359 360// Validate a chain including a pre-issuer as produced by Google's Compliance Monitor. 361func TestCMPreIssuedCert(t *testing.T) { 362 var b64Chain = []string{ 363 "MIID+jCCAuKgAwIBAgIHBWW7shJizTANBgkqhkiG9w0BAQsFADB1MQswCQYDVQQGEwJHQjEPMA0GA1UEBwwGTG9uZG9uMTowOAYDVQQKDDFHb29nbGUgQ2VydGlmaWNhdGUgVHJhbnNwYXJlbmN5IChQcmVjZXJ0IFNpZ25pbmcpMRkwFwYDVQQFExAxNTE5MjMxNzA0MTczNDg3MB4XDTE4MDIyMTE2NDgyNFoXDTE4MTIwMTIwMzMyN1owYzELMAkGA1UEBhMCR0IxDzANBgNVBAcMBkxvbmRvbjEoMCYGA1UECgwfR29vZ2xlIENlcnRpZmljYXRlIFRyYW5zcGFyZW5jeTEZMBcGA1UEBRMQMTUxOTIzMTcwNDM5MjM5NzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKnKP9TP6hkEuD+d1rPeA8mxo5xFYffhCcEitP8PtTl7G2RqFrndPeAkzgvOxPB3Jrhx7LtMtg0IvS8y7Sy1qDqDou1/OrJgwCeWMc1/KSneuGP8GTX0Rqy4z8+LsiBN/tMDbt94RuiyCeltIAaHGmsNeYXV34ayD3vSIAQbtLUOD39KqrJWO0tQ//nshBuFlebiUrDP7rirPusYYW0stJKiCKeORhHvL3/I8mCYGNO0XIWMpASH2S9LGMwg+AQM13whC1KL65EGuVs4Ta0rO+Tl8Yi0is0RwdUmgdSGtl0evPTzyUXbA1n1BpkLcSQ5E3RxY3O6Ge9Whvtmg9vAJiMCAwEAAaOBoDCBnTATBgNVHSUEDDAKBggrBgEFBQcDATAjBgNVHREEHDAaghhmbG93ZXJzLXRvLXRoZS13b3JsZC5jb20wDAYDVR0TAQH/BAIwADAfBgNVHSMEGDAWgBRKCM/Ajh0Fu6FFjJ9F4gVWK2oj/jAdBgNVHQ4EFgQUVjYl6wDey3DxvmTG2HL4vdiUt+MwEwYKKwYBBAHWeQIEAwEB/wQCBQAwDQYJKoZIhvcNAQELBQADggEBAAvyEFDIAWr0URsZzrJLZEL8p6FMTzVxY/MOvGP8QMXA6xNVElxYnDPF32JERAl+poR7syByhVFcEjrw7f2FTlMc04+hT/hsYzi8cMAmfX9KA36xUBVjyqvqwofxTwoWYdf+eGZW0EG8Yp1pM7iUy9bdlh3sgdOpmT9Z5XGCRwvdW1+mctv0JMKDdWzxBqYyNMnNjvjHBmkiuHeDDGFsV2zq+wV64RwJa2eVrnkMDMV1mscL6KzNRLPP2ZpNz/8H7SPock+fk4cZrdqj+0IzFt+6ixSoKyltyD+nkbWjRGY4iyboo/nPgTQ1IQCS2OPVHWw3NijFD8hqgAnYvz0Dn+k=", 364 "MIIE4jCCAsqgAwIBAgIHBWW7sg8LrzANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJHQjEPMA0GA1UECAwGTG9uZG9uMRcwFQYDVQQKDA5Hb29nbGUgVUsgTHRkLjEhMB8GA1UECwwYQ2VydGlmaWNhdGUgVHJhbnNwYXJlbmN5MSMwIQYDVQQDDBpNZXJnZSBEZWxheSBJbnRlcm1lZGlhdGUgMTAeFw0xODAyMjExNjQ4MjRaFw0xODEyMDEyMDM0MjdaMHUxCzAJBgNVBAYTAkdCMQ8wDQYDVQQHDAZMb25kb24xOjA4BgNVBAoMMUdvb2dsZSBDZXJ0aWZpY2F0ZSBUcmFuc3BhcmVuY3kgKFByZWNlcnQgU2lnbmluZykxGTAXBgNVBAUTEDE1MTkyMzE3MDQxNzM0ODcwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCKWlc3A43kJ9IzmkCPXcsGwTxlIvtl9sNYBWlx9qqHa1i6tU6rZuH9uXAb3wsn39fqY22HzF/yrx9pd05doFfRq6dvvm4eHNFfFm4cJur1kmPe8vLKpSI/P2DPx4/mRzrHnPAI8Jo9QgKcj91AyYeB689ZFzH30ay32beo6PxQvtoJkzl+dzf9Hs1ezavS7nDCuqDnu1V1Og7J5xTHZeNyTKgD5Kx28ukmIp2wGOvg3omuInABg/ew0VxnG/txKV+69zfV9dhclU3m16L81e3RkJ8Kg4RLb0mh9X3EMn90SpJ9yw0j8FF0Esk6wxuYeUGLShUji8BPnnbactY9B6ORAgMBAAGjbTBrMBgGA1UdJQEB/wQOMAwGCisGAQQB1nkCBAQwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTpPAThgC/ChBMtJnCe8v0az6r+xjAdBgNVHQ4EFgQUSgjPwI4dBbuhRYyfReIFVitqI/4wDQYJKoZIhvcNAQEFBQADggIBAEep2uWAFsdq1nLtLWLGh7DfVPc/K+1lcqNx64ucjpVZbDnMnYKFagf2Z9rHEWqR7kwuLac5xW8woSlLa/NHmJmdg18HGUhlS+x8iMPv7dK6hfNsRFdjLZkZOFneuf9j1b0dV+rXoRvyY+Oq+lomC98bEr+g9zq+M7wJ4wS/KeaNHpPw1pBeTtCdw+1c4ZgRTOEa2OUUpkpueJ+9psD/hbp6HLF+WYijWQ0/iYSxJ4TbjTC+omKRsGhvxSLbP8cSMt3X1pJgrFK1BvH4lqqEXGDNEiVNoPCHraEa8JtMZIo47/Af13lDfp6sBdZ0lvLAVDduWgg/2RkWCbHefAe81h+cYdDS775TF2TCMTwsR6GsM9sVCbfPvHXI/pUzamRn0i0CrhyccBBdPrUhj+cXuc9kqSkLegun9D8EBDMM9va5wb1HM0ruSno+YuLtfhCdBRHr/RG2BKJi7uUDjJ8goHov/EUJmHjAIARKz74IPWRkxMrnOvGhnNa2Hz+da3hpusz0Mj4rsqv1EKTC2wbCs6Rk2MRPSxdRbywdWLSmGn249SMfXK4An+dqoRk1fwKqdXc4swoUvxnGUi5ajBaRtc6631zBTmvmSFQnvGmS42aF7q2PjfvWPIuO+d//m8KgN6o2YyjrdPDDslI2RZUE5ngOR+JynvhjYrrB7Bat1EY7", 365 "MIIFyDCCA7CgAwIBAgICEAEwDQYJKoZIhvcNAQEFBQAwfTELMAkGA1UEBhMCR0IxDzANBgNVBAgMBkxvbmRvbjEXMBUGA1UECgwOR29vZ2xlIFVLIEx0ZC4xITAfBgNVBAsMGENlcnRpZmljYXRlIFRyYW5zcGFyZW5jeTEhMB8GA1UEAwwYTWVyZ2UgRGVsYXkgTW9uaXRvciBSb290MB4XDTE0MDcxNzEyMjYzMFoXDTE5MDcxNjEyMjYzMFowfzELMAkGA1UEBhMCR0IxDzANBgNVBAgMBkxvbmRvbjEXMBUGA1UECgwOR29vZ2xlIFVLIEx0ZC4xITAfBgNVBAsMGENlcnRpZmljYXRlIFRyYW5zcGFyZW5jeTEjMCEGA1UEAwwaTWVyZ2UgRGVsYXkgSW50ZXJtZWRpYXRlIDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDB6HT+/5ru8wO7+mNFOIH6r43BwiwJZB2vQwOB8zvBV79sTIqNV7Grx5KFnSDyGRUJxZfEN7FGc96lr0vqFDlt1DbcYgVV15U+Dt4B9/+0Tz/3zeZO0kVjTg3wqvzpw6xetj2N4dlpysiFQZVAOp+dHUw9zu3xNR7dlFdDvFSrdFsgT7Uln+Pt9pXCz5C4hsSP9oC3RP7CaRtDRSQrMcNvMRi3J8XeXCXsGqMKTCRhxRGe9ruQ2Bbm5ExbmVW/ou00Fr9uSlPJL6+sDR8Li/PTW+DU9hygXSj8Zi36WI+6PuA4BHDAEt7Z5Ru/Hnol76dFeExJ0F6vjc7gUnNh7JExJgBelyz0uGORT4NhWC7SRWP/ngPFLoqcoyZMVsGGtOxSt+aVzkKuF+x64CVxMeHb9I8t3iQubpHqMEmIE1oVSCsF/AkTVTKLOeWG6N06SjoUy5fu9o+faXKMKR8hldLM5z1K6QhFsb/F+uBAuU/DWaKVEZgbmWautW06fF5I+OyoFeW+hrPTbmon4OLE3ubjDxKnyTa4yYytWSisojjfw5z58sUkbLu7KAy2+Z60m/0deAiVOQcsFkxwgzcXRt7bxN7By5Q5Bzrz8uYPjFBfBnlhqMU5RU/FNBFY7Mx4Uy8+OcMYfJQ5/A/4julXEx1HjfBj3VCyrT/noHDpBeOGiwIDAQABo1AwTjAdBgNVHQ4EFgQU6TwE4YAvwoQTLSZwnvL9Gs+q/sYwHwYDVR0jBBgwFoAU8197dUnjeEE5aiC2fGtMXMk9WEEwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAgEACFjL1UXy6S4JkGrDnz1VwTYHplFDY4bG6Q8Sh3Og6z9HJdivNft/iAQ2tIHyz0eAGCXeVPE/j1kgvz2RbnUxQd5eWdLeu/w/wiZyHxWhbTt6RhjqBVFjnx0st7n6rRt+Bw8jpugZfD11SbumVT/V20Gc45lHf2oEgbkPUcnTB9gssFz5Z4KKGs5lIHz4a20WeSJF3PJLTBefkRhHNufi/LhjpLXImwrC82g5ChBZS5XIVuJZx3VkMWiYz4emgX0YWF/JdtaB2dUQ7yrTforQ5J9b1JnJ7H/o9DsX3/ubfQ39gwDBxTicnqC+Q3Dcv3i9PvwjCNJQuGa7ygMcDEn/d6elQg2qHxtqRE02ZlOXTC0XnDAJhx7myJFA/Knv3yO9S4jG6665KG9Y88/CHkh08YLR7NYFiRmwOxjbe3lb6csl/FFmqUXvjhEzzWAxKjI09GSd9hZkB8u17Mg46eEYwF3ufIlqmYdlWufjSc2BZuaNNN6jtK6JKp8jhQUycehgtUK+NlBQOXTzu28miDdasoSH2mdR0PLDo1547+MLGdV4COvqLERTmQrYHrliicD5nFCA+CCSvGEjo0DGOmF/O8StwSmNiKJ4ppPvk2iGEdO07e0LbQI+2fbC6og2SDGXUlsbG85wqQw0A7CU1fQSqhFBuZZauDFMUvdy3v/BAIw=", 366 "MIIFzTCCA7WgAwIBAgIJAJ7TzLHRLKJyMA0GCSqGSIb3DQEBBQUAMH0xCzAJBgNVBAYTAkdCMQ8wDQYDVQQIDAZMb25kb24xFzAVBgNVBAoMDkdvb2dsZSBVSyBMdGQuMSEwHwYDVQQLDBhDZXJ0aWZpY2F0ZSBUcmFuc3BhcmVuY3kxITAfBgNVBAMMGE1lcmdlIERlbGF5IE1vbml0b3IgUm9vdDAeFw0xNDA3MTcxMjA1NDNaFw00MTEyMDIxMjA1NDNaMH0xCzAJBgNVBAYTAkdCMQ8wDQYDVQQIDAZMb25kb24xFzAVBgNVBAoMDkdvb2dsZSBVSyBMdGQuMSEwHwYDVQQLDBhDZXJ0aWZpY2F0ZSBUcmFuc3BhcmVuY3kxITAfBgNVBAMMGE1lcmdlIERlbGF5IE1vbml0b3IgUm9vdDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKoWHPIgXtgaxWVIPNpCaj2y5Yj9t1ixe5PqjWhJXVNKAbpPbNHA/AoSivecBm3FTD9DfgW6J17mHb+cvbKSgYNzgTk5e2GJrnOP7yubYJpt2OCw0OILJD25NsApzcIiCvLA4aXkqkGgBq9FiVfisReNJxVu8MtxfhbVQCXZf0PpkW+yQPuF99V5Ri+grHbHYlaEN1C/HM3+t2yMR4hkd2RNXsMjViit9qCchIi/pQNt5xeQgVGmtYXyc92ftTMrmvduj7+pHq9DEYFt3ifFxE8v0GzCIE1xR/d7prFqKl/KRwAjYUcpU4vuazywcmRxODKuwWFVDrUBkGgCIVIjrMJWStH5i7WTSSTrVtOD/HWYvkXInZlSgcDvsNIG0pptJaEKSP4jUzI3nFymnoNZn6pnfdIII/XISpYSVeyl1IcdVMod8HdKoRew9CzW6f2n6KSKU5I8X5QEM1NUTmRLWmVi5c75/CvS/PzOMyMzXPf+fE2Dwbf4OcR5AZLTupqp8yCTqo7ny+cIBZ1TjcZjzKG4JTMaqDZ1Sg0T3mO/ZbbiBE3N8EHxoMWpw8OP50z1dtRRwj6qUZ2zLvngOb2EihlMO15BpVZC3Cg929c9Hdl65pUd4YrYnQBQB/rn6IvHo8zot8zElgOg22fHbViijUt3qnRggB40N30MXkYGwuJbAgMBAAGjUDBOMB0GA1UdDgQWBBTzX3t1SeN4QTlqILZ8a0xcyT1YQTAfBgNVHSMEGDAWgBTzX3t1SeN4QTlqILZ8a0xcyT1YQTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4ICAQB3HP6jRXmpdSDYwkI9aOzQeJH4x/HDi/PNMOqdNje/xdNzUy7HZWVYvvSVBkZ1DG/ghcUtn/wJ5m6/orBn3ncnyzgdKyXbWLnCGX/V61PgIPQpuGo7HzegenYaZqWz7NeXxGaVo3/y1HxUEmvmvSiioQM1cifGtz9/aJsJtIkn5umlImenKKEV1Ly7R3Uz3Cjz/Ffac1o+xU+8NpkLF/67fkazJCCMH6dCWgy6SL3AOB6oKFIVJhw8SD8vptHaDbpJSRBxifMtcop/85XUNDCvO4zkvlB1vPZ9ZmYZQdyL43NA+PkoKy0qrdaQZZMq1Jdp+Lx/yeX255/zkkILp43jFyd44rZ+TfGEQN1WHlp4RMjvoGwOX1uGlfoGkRSgBRj7TBn514VYMbXu687RS4WY2v+kny3PUFv/ZBfYSyjoNZnU4Dce9kstgv+gaKMQRPcyL+4vZU7DV8nBIfNFilCXKMN/VnNBKtDV52qmtOsVghgai+QE09w15x7dg+44gIfWFHxNhvHKys+s4BBN8fSxAMLOsb5NGFHE8x58RAkmIYWHjyPM6zB5AUPw1b2A0sDtQmCqoxJZfZUKrzyLz8gS2aVujRYN13KklHQ3EKfkeKBG2KXVBe5rjMN/7Anf1MtXxsTY6O8qIuHZ5QlXhSYzE41yIlPlG6d7AGnTiBIgeg==", 367 } 368 rawChain := make([][]byte, len(b64Chain)) 369 for i, b64Data := range b64Chain { 370 var err error 371 rawChain[i], err = base64.StdEncoding.DecodeString(b64Data) 372 if err != nil { 373 t.Fatalf("failed to base64.Decode(chain[%d]): %v", i, err) 374 } 375 } 376 377 root, err := x509.ParseCertificate(rawChain[len(rawChain)-1]) 378 if err != nil { 379 t.Fatalf("failed to parse root cert: %v", err) 380 } 381 cmRoot := NewPEMCertPool() 382 cmRoot.AddCert(root) 383 opts := CertValidationOpts{trustedRoots: cmRoot} 384 chain, err := ValidateChain(rawChain, opts) 385 if err != nil { 386 t.Fatalf("failed to ValidateChain: %v", err) 387 } 388 for i, c := range chain { 389 t.Logf("chain[%d] = \n%s", i, x509util.CertificateToString(c)) 390 } 391} 392