...

Source file src/github.com/google/certificate-transparency-go/trillian/ctfe/cert_checker_test.go

Documentation: github.com/google/certificate-transparency-go/trillian/ctfe

     1  // Copyright 2016 Google LLC. 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  
    15  package ctfe
    16  
    17  import (
    18  	"encoding/base64"
    19  	"encoding/pem"
    20  	"strings"
    21  	"testing"
    22  	"time"
    23  
    24  	"github.com/google/certificate-transparency-go/asn1"
    25  	"github.com/google/certificate-transparency-go/trillian/ctfe/testonly"
    26  	"github.com/google/certificate-transparency-go/x509"
    27  	"github.com/google/certificate-transparency-go/x509/pkix"
    28  	"github.com/google/certificate-transparency-go/x509util"
    29  )
    30  
    31  func wipeExtensions(cert *x509.Certificate) *x509.Certificate {
    32  	cert.Extensions = cert.Extensions[:0]
    33  	return cert
    34  }
    35  
    36  func makePoisonNonCritical(cert *x509.Certificate) *x509.Certificate {
    37  	// Invalid as a pre-cert because poison extension needs to be marked as critical.
    38  	cert.Extensions = []pkix.Extension{{Id: x509.OIDExtensionCTPoison, Critical: false, Value: asn1.NullBytes}}
    39  	return cert
    40  }
    41  
    42  func makePoisonNonNull(cert *x509.Certificate) *x509.Certificate {
    43  	// Invalid as a pre-cert because poison extension is not ASN.1 NULL value.
    44  	cert.Extensions = []pkix.Extension{{Id: x509.OIDExtensionCTPoison, Critical: false, Value: []byte{0x42, 0x42, 0x42}}}
    45  	return cert
    46  }
    47  
    48  func TestIsPrecertificate(t *testing.T) {
    49  	var tests = []struct {
    50  		desc        string
    51  		cert        *x509.Certificate
    52  		wantPrecert bool
    53  		wantErr     bool
    54  	}{
    55  		{
    56  			desc:        "valid-precert",
    57  			cert:        pemToCert(t, testonly.PrecertPEMValid),
    58  			wantPrecert: true,
    59  		},
    60  		{
    61  			desc:        "valid-cert",
    62  			cert:        pemToCert(t, testonly.CACertPEM),
    63  			wantPrecert: false,
    64  		},
    65  		{
    66  			desc:        "remove-exts-from-precert",
    67  			cert:        wipeExtensions(pemToCert(t, testonly.PrecertPEMValid)),
    68  			wantPrecert: false,
    69  		},
    70  		{
    71  			desc:        "poison-non-critical",
    72  			cert:        makePoisonNonCritical(pemToCert(t, testonly.PrecertPEMValid)),
    73  			wantPrecert: false,
    74  			wantErr:     true,
    75  		},
    76  		{
    77  			desc:        "poison-non-null",
    78  			cert:        makePoisonNonNull(pemToCert(t, testonly.PrecertPEMValid)),
    79  			wantPrecert: false,
    80  			wantErr:     true,
    81  		},
    82  	}
    83  
    84  	for _, test := range tests {
    85  		gotPrecert, err := IsPrecertificate(test.cert)
    86  		t.Run(test.desc, func(t *testing.T) {
    87  			if err != nil {
    88  				if !test.wantErr {
    89  					t.Errorf("IsPrecertificate()=%v,%v; want %v,nil", gotPrecert, err, test.wantPrecert)
    90  				}
    91  				return
    92  			}
    93  			if test.wantErr {
    94  				t.Errorf("IsPrecertificate()=%v,%v; want _,%v", gotPrecert, err, test.wantErr)
    95  			}
    96  			if gotPrecert != test.wantPrecert {
    97  				t.Errorf("IsPrecertificate()=%v,%v; want %v,nil", gotPrecert, err, test.wantPrecert)
    98  			}
    99  		})
   100  	}
   101  }
   102  
   103  func TestValidateChain(t *testing.T) {
   104  	fakeCARoots := x509util.NewPEMCertPool()
   105  	if !fakeCARoots.AppendCertsFromPEM([]byte(testonly.FakeCACertPEM)) {
   106  		t.Fatal("failed to load fake root")
   107  	}
   108  	if !fakeCARoots.AppendCertsFromPEM([]byte(testonly.FakeRootCACertPEM)) {
   109  		t.Fatal("failed to load fake root")
   110  	}
   111  	if !fakeCARoots.AppendCertsFromPEM([]byte(testonly.CACertPEM)) {
   112  		t.Fatal("failed to load CA root")
   113  	}
   114  	if !fakeCARoots.AppendCertsFromPEM([]byte(testonly.RealPrecertIntermediatePEM)) {
   115  		t.Fatal("failed to load real intermediate")
   116  	}
   117  	validateOpts := CertValidationOpts{
   118  		trustedRoots: fakeCARoots,
   119  	}
   120  
   121  	var tests = []struct {
   122  		desc        string
   123  		chain       [][]byte
   124  		wantErr     bool
   125  		wantPathLen int
   126  		modifyOpts  func(v *CertValidationOpts)
   127  	}{
   128  		{
   129  			desc:    "missing-intermediate-cert",
   130  			chain:   pemsToDERChain(t, []string{testonly.LeafSignedByFakeIntermediateCertPEM}),
   131  			wantErr: true,
   132  		},
   133  		{
   134  			desc:    "wrong-cert-order",
   135  			chain:   pemsToDERChain(t, []string{testonly.FakeIntermediateCertPEM, testonly.LeafSignedByFakeIntermediateCertPEM}),
   136  			wantErr: true,
   137  		},
   138  		{
   139  			desc:    "unrelated-cert-in-chain",
   140  			chain:   pemsToDERChain(t, []string{testonly.FakeIntermediateCertPEM, testonly.TestCertPEM}),
   141  			wantErr: true,
   142  		},
   143  		{
   144  			desc:    "unrelated-cert-after-chain",
   145  			chain:   pemsToDERChain(t, []string{testonly.LeafSignedByFakeIntermediateCertPEM, testonly.FakeIntermediateCertPEM, testonly.TestCertPEM}),
   146  			wantErr: true,
   147  		},
   148  		{
   149  			desc:        "valid-chain",
   150  			chain:       pemsToDERChain(t, []string{testonly.LeafSignedByFakeIntermediateCertPEM, testonly.FakeIntermediateCertPEM}),
   151  			wantPathLen: 3,
   152  		},
   153  		{
   154  			desc:        "valid-chain-with-policyconstraints",
   155  			chain:       pemsToDERChain(t, []string{testonly.LeafCertPEM, testonly.FakeIntermediateWithPolicyConstraintsCertPEM}),
   156  			wantPathLen: 3,
   157  		},
   158  		{
   159  			desc:        "valid-chain-with-policyconstraints-inc-root",
   160  			chain:       pemsToDERChain(t, []string{testonly.LeafCertPEM, testonly.FakeIntermediateWithPolicyConstraintsCertPEM, testonly.FakeRootCACertPEM}),
   161  			wantPathLen: 3,
   162  		},
   163  		{
   164  			desc:        "valid-chain-with-nameconstraints",
   165  			chain:       pemsToDERChain(t, []string{testonly.LeafCertPEM, testonly.FakeIntermediateWithNameConstraintsCertPEM}),
   166  			wantPathLen: 3,
   167  		},
   168  		{
   169  			desc:        "chain-with-invalid-nameconstraints",
   170  			chain:       pemsToDERChain(t, []string{testonly.LeafCertPEM, testonly.FakeIntermediateWithInvalidNameConstraintsCertPEM}),
   171  			wantPathLen: 3,
   172  		},
   173  		{
   174  			desc:        "chain-of-len-4",
   175  			chain:       pemFileToDERChain(t, "../testdata/subleaf.chain"),
   176  			wantPathLen: 4,
   177  		},
   178  		{
   179  			desc:    "misordered-chain-of-len-4",
   180  			chain:   pemFileToDERChain(t, "../testdata/subleaf.misordered.chain"),
   181  			wantErr: true,
   182  		},
   183  		{
   184  			desc:  "reject-non-existent-ext-id",
   185  			chain: pemsToDERChain(t, []string{testonly.LeafSignedByFakeIntermediateCertPEM, testonly.FakeIntermediateCertPEM}),
   186  			modifyOpts: func(v *CertValidationOpts) {
   187  				// reject SubjectKeyIdentifier extension
   188  				v.rejectExtIds = []asn1.ObjectIdentifier{[]int{99, 99, 99, 99}}
   189  			},
   190  			wantPathLen: 3,
   191  		},
   192  		{
   193  			desc:  "reject-non-existent-ext-id-precert",
   194  			chain: pemsToDERChain(t, []string{testonly.PrecertPEMValid}),
   195  			modifyOpts: func(v *CertValidationOpts) {
   196  				// reject SubjectKeyIdentifier extension
   197  				v.rejectExtIds = []asn1.ObjectIdentifier{[]int{99, 99, 99, 99}}
   198  			},
   199  			wantPathLen: 2,
   200  		},
   201  		{
   202  			desc:    "reject-ext-id",
   203  			chain:   pemsToDERChain(t, []string{testonly.LeafSignedByFakeIntermediateCertPEM, testonly.FakeIntermediateCertPEM}),
   204  			wantErr: true,
   205  			modifyOpts: func(v *CertValidationOpts) {
   206  				// reject SubjectKeyIdentifier extension
   207  				v.rejectExtIds = []asn1.ObjectIdentifier{[]int{2, 5, 29, 14}}
   208  			},
   209  		},
   210  		{
   211  			desc:    "reject-ext-id-precert",
   212  			chain:   pemsToDERChain(t, []string{testonly.PrecertPEMValid}),
   213  			wantErr: true,
   214  			modifyOpts: func(v *CertValidationOpts) {
   215  				// reject SubjectKeyIdentifier extension
   216  				v.rejectExtIds = []asn1.ObjectIdentifier{[]int{2, 5, 29, 14}}
   217  			},
   218  		},
   219  		{
   220  			desc:    "reject-eku-not-present-in-cert",
   221  			chain:   pemsToDERChain(t, []string{testonly.LeafSignedByFakeIntermediateCertPEM, testonly.FakeIntermediateCertPEM}),
   222  			wantErr: true,
   223  			modifyOpts: func(v *CertValidationOpts) {
   224  				// reject cert without ExtKeyUsageEmailProtection
   225  				v.extKeyUsages = []x509.ExtKeyUsage{x509.ExtKeyUsageEmailProtection}
   226  			},
   227  		},
   228  		{
   229  			desc:        "allow-eku-present-in-cert",
   230  			chain:       pemsToDERChain(t, []string{testonly.LeafSignedByFakeIntermediateCertPEM, testonly.FakeIntermediateCertPEM}),
   231  			wantPathLen: 3,
   232  			modifyOpts: func(v *CertValidationOpts) {
   233  				v.extKeyUsages = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}
   234  			},
   235  		},
   236  		{
   237  			desc:    "reject-eku-not-present-in-precert",
   238  			chain:   pemsToDERChain(t, []string{testonly.RealPrecertWithEKUPEM}),
   239  			wantErr: true,
   240  			modifyOpts: func(v *CertValidationOpts) {
   241  				// reject cert without ExtKeyUsageEmailProtection
   242  				v.extKeyUsages = []x509.ExtKeyUsage{x509.ExtKeyUsageEmailProtection}
   243  			},
   244  		},
   245  		{
   246  			desc:        "allow-eku-present-in-precert",
   247  			chain:       pemsToDERChain(t, []string{testonly.RealPrecertWithEKUPEM}),
   248  			wantPathLen: 2,
   249  			modifyOpts: func(v *CertValidationOpts) {
   250  				v.extKeyUsages = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}
   251  			},
   252  		},
   253  	}
   254  	for _, test := range tests {
   255  		t.Run(test.desc, func(t *testing.T) {
   256  			validateOpts := validateOpts
   257  			if test.modifyOpts != nil {
   258  				test.modifyOpts(&validateOpts)
   259  			}
   260  			gotPath, err := ValidateChain(test.chain, validateOpts)
   261  			if err != nil {
   262  				if !test.wantErr {
   263  					t.Errorf("ValidateChain()=%v,%v; want _,nil", gotPath, err)
   264  				}
   265  				return
   266  			}
   267  			if test.wantErr {
   268  				t.Errorf("ValidateChain()=%v,%v; want _,non-nil", gotPath, err)
   269  				return
   270  			}
   271  			if len(gotPath) != test.wantPathLen {
   272  				t.Errorf("|ValidateChain()|=%d; want %d", len(gotPath), test.wantPathLen)
   273  				for _, c := range gotPath {
   274  					t.Logf("Subject: %s Issuer: %s", x509util.NameToString(c.Subject), x509util.NameToString(c.Issuer))
   275  				}
   276  			}
   277  		})
   278  	}
   279  }
   280  
   281  func TestCA(t *testing.T) {
   282  	fakeCARoots := x509util.NewPEMCertPool()
   283  	if !fakeCARoots.AppendCertsFromPEM([]byte(testonly.FakeCACertPEM)) {
   284  		t.Fatal("failed to load fake root")
   285  	}
   286  	validateOpts := CertValidationOpts{
   287  		trustedRoots: fakeCARoots,
   288  	}
   289  	chain := pemsToDERChain(t, []string{testonly.LeafSignedByFakeIntermediateCertPEM, testonly.FakeIntermediateCertPEM})
   290  	leaf, err := x509.ParseCertificate(chain[0])
   291  	if x509.IsFatal(err) {
   292  		t.Fatalf("Failed to parse golden certificate DER: %v", err)
   293  	}
   294  	t.Logf("Cert expiry date: %v", leaf.NotAfter)
   295  
   296  	var tests = []struct {
   297  		desc    string
   298  		chain   [][]byte
   299  		caOnly  bool
   300  		wantErr bool
   301  	}{
   302  		{
   303  			desc:  "end-entity, allow non-CA",
   304  			chain: chain,
   305  		},
   306  		{
   307  			desc:    "end-entity, disallow non-CA",
   308  			chain:   chain,
   309  			caOnly:  true,
   310  			wantErr: true,
   311  		},
   312  		{
   313  			desc:  "intermediate, allow non-CA",
   314  			chain: chain[1:],
   315  		},
   316  		{
   317  			desc:   "intermediate, disallow non-CA",
   318  			chain:  chain[1:],
   319  			caOnly: true,
   320  		},
   321  	}
   322  	for _, test := range tests {
   323  		t.Run(test.desc, func(t *testing.T) {
   324  			validateOpts.acceptOnlyCA = test.caOnly
   325  			gotPath, err := ValidateChain(test.chain, validateOpts)
   326  			if err != nil {
   327  				if !test.wantErr {
   328  					t.Errorf("ValidateChain()=%v,%v; want _,nil", gotPath, err)
   329  				}
   330  				return
   331  			}
   332  			if test.wantErr {
   333  				t.Errorf("ValidateChain()=%v,%v; want _,non-nil", gotPath, err)
   334  			}
   335  		})
   336  	}
   337  }
   338  
   339  func TestNotAfterRange(t *testing.T) {
   340  	fakeCARoots := x509util.NewPEMCertPool()
   341  	if !fakeCARoots.AppendCertsFromPEM([]byte(testonly.FakeCACertPEM)) {
   342  		t.Fatal("failed to load fake root")
   343  	}
   344  	validateOpts := CertValidationOpts{
   345  		trustedRoots:  fakeCARoots,
   346  		rejectExpired: false,
   347  	}
   348  
   349  	chain := pemsToDERChain(t, []string{testonly.LeafSignedByFakeIntermediateCertPEM, testonly.FakeIntermediateCertPEM})
   350  
   351  	var tests = []struct {
   352  		desc          string
   353  		chain         [][]byte
   354  		notAfterStart time.Time
   355  		notAfterLimit time.Time
   356  		wantErr       bool
   357  	}{
   358  		{
   359  			desc:  "valid-chain, no range",
   360  			chain: chain,
   361  		},
   362  		{
   363  			desc:          "valid-chain, valid range",
   364  			chain:         chain,
   365  			notAfterStart: time.Date(2018, 1, 1, 0, 0, 0, 0, time.UTC),
   366  			notAfterLimit: time.Date(2020, 7, 1, 0, 0, 0, 0, time.UTC),
   367  		},
   368  		{
   369  			desc:          "before valid range",
   370  			chain:         chain,
   371  			notAfterStart: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC),
   372  			wantErr:       true,
   373  		},
   374  		{
   375  			desc:          "after valid range",
   376  			chain:         chain,
   377  			notAfterLimit: time.Date(1999, 1, 1, 0, 0, 0, 0, time.UTC),
   378  			wantErr:       true,
   379  		},
   380  	}
   381  	for _, test := range tests {
   382  		t.Run(test.desc, func(t *testing.T) {
   383  			if !test.notAfterStart.IsZero() {
   384  				validateOpts.notAfterStart = &test.notAfterStart
   385  			}
   386  			if !test.notAfterLimit.IsZero() {
   387  				validateOpts.notAfterLimit = &test.notAfterLimit
   388  			}
   389  			gotPath, err := ValidateChain(test.chain, validateOpts)
   390  			if err != nil {
   391  				if !test.wantErr {
   392  					t.Errorf("ValidateChain()=%v,%v; want _,nil", gotPath, err)
   393  				}
   394  				return
   395  			}
   396  			if test.wantErr {
   397  				t.Errorf("ValidateChain()=%v,%v; want _,non-nil", gotPath, err)
   398  			}
   399  		})
   400  	}
   401  }
   402  
   403  func TestRejectExpiredUnexpired(t *testing.T) {
   404  	fakeCARoots := x509util.NewPEMCertPool()
   405  	// Validity period: Jul 11, 2016 - Jul 11, 2017.
   406  	if !fakeCARoots.AppendCertsFromPEM([]byte(testonly.FakeCACertPEM)) {
   407  		t.Fatal("failed to load fake root")
   408  	}
   409  	// Validity period: May 13, 2016 - Jul 12, 2019.
   410  	chain := pemsToDERChain(t, []string{testonly.LeafSignedByFakeIntermediateCertPEM, testonly.FakeIntermediateCertPEM})
   411  	validateOpts := CertValidationOpts{
   412  		trustedRoots: fakeCARoots,
   413  		extKeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
   414  	}
   415  	beforeValidPeriod := time.Date(1999, 1, 1, 0, 0, 0, 0, time.UTC)
   416  	currentValidPeriod := time.Date(2017, 1, 1, 0, 0, 0, 0, time.UTC)
   417  	afterValidPeriod := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)
   418  
   419  	for _, tc := range []struct {
   420  		desc            string
   421  		rejectExpired   bool
   422  		rejectUnexpired bool
   423  		now             time.Time
   424  		wantErr         string
   425  	}{
   426  		// No flags: accept anything.
   427  		{
   428  			desc: "no-reject-current",
   429  			now:  currentValidPeriod,
   430  		},
   431  		{
   432  			desc: "no-reject-after",
   433  			now:  afterValidPeriod,
   434  		},
   435  		{
   436  			desc: "no-reject-before",
   437  			now:  beforeValidPeriod,
   438  		},
   439  		// Reject-Expired: only allow currently-valid and not yet valid
   440  		{
   441  			desc:          "reject-expired-current",
   442  			rejectExpired: true,
   443  			now:           currentValidPeriod,
   444  		},
   445  		{
   446  			desc:          "reject-expired-after",
   447  			rejectExpired: true,
   448  			now:           afterValidPeriod,
   449  			wantErr:       "rejecting expired certificate",
   450  		},
   451  		{
   452  			desc:          "reject-expired-before",
   453  			rejectExpired: true,
   454  			now:           beforeValidPeriod,
   455  		},
   456  		// Reject-Unexpired: only allow expired
   457  		{
   458  			desc:            "reject-non-expired-after",
   459  			rejectUnexpired: true,
   460  			now:             afterValidPeriod,
   461  		},
   462  		{
   463  			desc:            "reject-non-expired-before",
   464  			rejectUnexpired: true,
   465  			now:             beforeValidPeriod,
   466  			wantErr:         "rejecting unexpired certificate",
   467  		},
   468  		{
   469  			desc:            "reject-non-expired-current",
   470  			rejectUnexpired: true,
   471  			now:             currentValidPeriod,
   472  			wantErr:         "rejecting unexpired certificate",
   473  		},
   474  		// Reject-Expired AND Reject-Unexpired: nothing allowed
   475  		{
   476  			desc:            "reject-all-after",
   477  			rejectExpired:   true,
   478  			rejectUnexpired: true,
   479  			now:             afterValidPeriod,
   480  			wantErr:         "rejecting expired certificate",
   481  		},
   482  		{
   483  			desc:            "reject-all-before",
   484  			rejectExpired:   true,
   485  			rejectUnexpired: true,
   486  			now:             beforeValidPeriod,
   487  			wantErr:         "rejecting unexpired certificate",
   488  		},
   489  		{
   490  			desc:            "reject-all-current",
   491  			rejectExpired:   true,
   492  			rejectUnexpired: true,
   493  			now:             currentValidPeriod,
   494  			wantErr:         "rejecting unexpired certificate",
   495  		},
   496  	} {
   497  		t.Run(tc.desc, func(t *testing.T) {
   498  			validateOpts.currentTime = tc.now
   499  			validateOpts.rejectExpired = tc.rejectExpired
   500  			validateOpts.rejectUnexpired = tc.rejectUnexpired
   501  			_, err := ValidateChain(chain, validateOpts)
   502  			if err != nil {
   503  				if len(tc.wantErr) == 0 {
   504  					t.Errorf("ValidateChain()=_,%v; want _,nil", err)
   505  				} else if !strings.Contains(err.Error(), tc.wantErr) {
   506  					t.Errorf("ValidateChain()=_,%v; want err containing %q", err, tc.wantErr)
   507  				}
   508  			} else if len(tc.wantErr) != 0 {
   509  				t.Errorf("ValidateChain()=_,nil; want err containing %q", tc.wantErr)
   510  			}
   511  		})
   512  	}
   513  }
   514  
   515  // Builds a chain of DER-encoded certs.
   516  // Note: ordering is important
   517  func pemsToDERChain(t *testing.T, pemCerts []string) [][]byte {
   518  	t.Helper()
   519  	chain := make([][]byte, 0, len(pemCerts))
   520  	for _, pemCert := range pemCerts {
   521  		cert := pemToCert(t, pemCert)
   522  		chain = append(chain, cert.Raw)
   523  	}
   524  	return chain
   525  }
   526  
   527  func pemToCert(t *testing.T, pemData string) *x509.Certificate {
   528  	t.Helper()
   529  	bytes, rest := pem.Decode([]byte(pemData))
   530  	if len(rest) > 0 {
   531  		t.Fatalf("Extra data after PEM: %v", rest)
   532  		return nil
   533  	}
   534  
   535  	cert, err := x509.ParseCertificate(bytes.Bytes)
   536  	if x509.IsFatal(err) {
   537  		t.Fatal(err)
   538  	}
   539  
   540  	return cert
   541  }
   542  
   543  func pemFileToDERChain(t *testing.T, filename string) [][]byte {
   544  	t.Helper()
   545  	rawChain, err := x509util.ReadPossiblePEMFile(filename, "CERTIFICATE")
   546  	if err != nil {
   547  		t.Fatalf("failed to load testdata: %v", err)
   548  	}
   549  	return rawChain
   550  }
   551  
   552  // Validate a chain including a pre-issuer as produced by Google's Compliance Monitor.
   553  func TestCMPreIssuedCert(t *testing.T) {
   554  	var b64Chain = []string{
   555  		"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=",
   556  		"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",
   557  		"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=",
   558  		"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==",
   559  	}
   560  	rawChain := make([][]byte, len(b64Chain))
   561  	for i, b64Data := range b64Chain {
   562  		var err error
   563  		rawChain[i], err = base64.StdEncoding.DecodeString(b64Data)
   564  		if err != nil {
   565  			t.Fatalf("failed to base64.Decode(chain[%d]): %v", i, err)
   566  		}
   567  	}
   568  
   569  	root, err := x509.ParseCertificate(rawChain[len(rawChain)-1])
   570  	if err != nil {
   571  		t.Fatalf("failed to parse root cert: %v", err)
   572  	}
   573  	cmRoot := x509util.NewPEMCertPool()
   574  	cmRoot.AddCert(root)
   575  
   576  	for _, tc := range []struct {
   577  		desc string
   578  		eku  []x509.ExtKeyUsage
   579  	}{
   580  		{
   581  			desc: "no EKU specified",
   582  		}, {
   583  			desc: "EKU ServerAuth",
   584  			eku:  []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
   585  		},
   586  	} {
   587  		t.Run(tc.desc, func(t *testing.T) {
   588  			opts := CertValidationOpts{
   589  				trustedRoots: cmRoot,
   590  				extKeyUsages: tc.eku,
   591  			}
   592  			chain, err := ValidateChain(rawChain, opts)
   593  			if err != nil {
   594  				t.Fatalf("failed to ValidateChain: %v", err)
   595  			}
   596  			for i, c := range chain {
   597  				t.Logf("chain[%d] = \n%s", i, x509util.CertificateToString(c))
   598  			}
   599  		})
   600  	}
   601  }
   602  

View as plain text