-
Notifications
You must be signed in to change notification settings - Fork 17.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
crypto/x509: verification with system and custom roots
Make system cert pools special, such that when one has extra roots added to it we run verifications twice, once using the platform verifier, if available, and once using the Go verifier, merging the results. This change re-enables SystemCertPool on Windows, but explicitly does not return anything from CertPool.Subjects (which matches the behavior of macOS). CertPool.Subjects is also marked deprecated. Fixes #46287 Fixes #16736 Change-Id: Idc1843f715ae2b2d0108e55ab942c287181a340a Reviewed-on: https://go-review.googlesource.com/c/go/+/353589 Reviewed-by: Filippo Valsorda <filippo@golang.org> Trust: Roland Shoemaker <roland@golang.org>
- Loading branch information
1 parent
4f083c7
commit 3544082
Showing
6 changed files
with
229 additions
and
62 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
// Copyright 2011 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package x509_test | ||
|
||
import ( | ||
"crypto/ecdsa" | ||
"crypto/elliptic" | ||
"crypto/rand" | ||
"crypto/tls" | ||
"crypto/x509" | ||
"crypto/x509/pkix" | ||
"internal/testenv" | ||
"math/big" | ||
"runtime" | ||
"testing" | ||
"time" | ||
) | ||
|
||
func TestHybridPool(t *testing.T) { | ||
if !(runtime.GOOS == "windows" || runtime.GOOS == "darwin" || runtime.GOOS == "ios") { | ||
t.Skipf("platform verifier not available on %s", runtime.GOOS) | ||
} | ||
if !testenv.HasExternalNetwork() { | ||
t.Skip() | ||
} | ||
|
||
// Get the google.com chain, which should be valid on all platforms we | ||
// are testing | ||
c, err := tls.Dial("tcp", "google.com:443", &tls.Config{InsecureSkipVerify: true}) | ||
if err != nil { | ||
t.Fatalf("tls connection failed: %s", err) | ||
} | ||
googChain := c.ConnectionState().PeerCertificates | ||
|
||
rootTmpl := &x509.Certificate{ | ||
SerialNumber: big.NewInt(1), | ||
Subject: pkix.Name{CommonName: "Go test root"}, | ||
IsCA: true, | ||
BasicConstraintsValid: true, | ||
NotBefore: time.Now().Add(-time.Hour), | ||
NotAfter: time.Now().Add(time.Hour * 10), | ||
} | ||
k, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) | ||
if err != nil { | ||
t.Fatalf("failed to generate test key: %s", err) | ||
} | ||
rootDER, err := x509.CreateCertificate(rand.Reader, rootTmpl, rootTmpl, k.Public(), k) | ||
if err != nil { | ||
t.Fatalf("failed to create test cert: %s", err) | ||
} | ||
root, err := x509.ParseCertificate(rootDER) | ||
if err != nil { | ||
t.Fatalf("failed to parse test cert: %s", err) | ||
} | ||
|
||
pool, err := x509.SystemCertPool() | ||
if err != nil { | ||
t.Fatalf("SystemCertPool failed: %s", err) | ||
} | ||
opts := x509.VerifyOptions{Roots: pool} | ||
|
||
_, err = googChain[0].Verify(opts) | ||
if err != nil { | ||
t.Fatalf("verification failed for google.com chain (empty pool): %s", err) | ||
} | ||
|
||
pool.AddCert(root) | ||
|
||
_, err = googChain[0].Verify(opts) | ||
if err != nil { | ||
t.Fatalf("verification failed for google.com chain (hybrid pool): %s", err) | ||
} | ||
|
||
certTmpl := &x509.Certificate{ | ||
SerialNumber: big.NewInt(1), | ||
NotBefore: time.Now().Add(-time.Hour), | ||
NotAfter: time.Now().Add(time.Hour * 10), | ||
DNSNames: []string{"example.com"}, | ||
} | ||
certDER, err := x509.CreateCertificate(rand.Reader, certTmpl, rootTmpl, k.Public(), k) | ||
if err != nil { | ||
t.Fatalf("failed to create test cert: %s", err) | ||
} | ||
cert, err := x509.ParseCertificate(certDER) | ||
if err != nil { | ||
t.Fatalf("failed to parse test cert: %s", err) | ||
} | ||
|
||
_, err = cert.Verify(opts) | ||
if err != nil { | ||
t.Fatalf("verification failed for custom chain (hybrid pool): %s", err) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
// Copyright 2021 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package x509_test | ||
|
||
import ( | ||
"crypto/tls" | ||
"crypto/x509" | ||
"internal/testenv" | ||
"testing" | ||
"time" | ||
) | ||
|
||
func TestPlatformVerifier(t *testing.T) { | ||
if !testenv.HasExternalNetwork() { | ||
t.Skip() | ||
} | ||
|
||
getChain := func(host string) []*x509.Certificate { | ||
t.Helper() | ||
c, err := tls.Dial("tcp", host+":443", &tls.Config{InsecureSkipVerify: true}) | ||
if err != nil { | ||
t.Fatalf("tls connection failed: %s", err) | ||
} | ||
return c.ConnectionState().PeerCertificates | ||
} | ||
|
||
tests := []struct { | ||
name string | ||
host string | ||
verifyName string | ||
verifyTime time.Time | ||
expectedErr string | ||
}{ | ||
{ | ||
// whatever google.com serves should, hopefully, be trusted | ||
name: "valid chain", | ||
host: "google.com", | ||
}, | ||
{ | ||
name: "expired leaf", | ||
host: "expired.badssl.com", | ||
expectedErr: "x509: certificate has expired or is not yet valid: ", | ||
}, | ||
{ | ||
name: "wrong host for leaf", | ||
host: "wrong.host.badssl.com", | ||
verifyName: "wrong.host.badssl.com", | ||
expectedErr: "x509: certificate is valid for *.badssl.com, badssl.com, not wrong.host.badssl.com", | ||
}, | ||
{ | ||
name: "self-signed leaf", | ||
host: "self-signed.badssl.com", | ||
expectedErr: "x509: certificate signed by unknown authority", | ||
}, | ||
{ | ||
name: "untrusted root", | ||
host: "untrusted-root.badssl.com", | ||
expectedErr: "x509: certificate signed by unknown authority", | ||
}, | ||
{ | ||
name: "expired leaf (custom time)", | ||
host: "google.com", | ||
verifyTime: time.Time{}.Add(time.Hour), | ||
expectedErr: "x509: certificate has expired or is not yet valid: ", | ||
}, | ||
{ | ||
name: "valid chain (custom time)", | ||
host: "google.com", | ||
verifyTime: time.Now(), | ||
}, | ||
} | ||
|
||
for _, tc := range tests { | ||
t.Run(tc.name, func(t *testing.T) { | ||
chain := getChain(tc.host) | ||
var opts x509.VerifyOptions | ||
if len(chain) > 1 { | ||
opts.Intermediates = x509.NewCertPool() | ||
for _, c := range chain[1:] { | ||
opts.Intermediates.AddCert(c) | ||
} | ||
} | ||
if tc.verifyName != "" { | ||
opts.DNSName = tc.verifyName | ||
} | ||
if !tc.verifyTime.IsZero() { | ||
opts.CurrentTime = tc.verifyTime | ||
} | ||
|
||
_, err := chain[0].Verify(opts) | ||
if err != nil && tc.expectedErr == "" { | ||
t.Errorf("unexpected verification error: %s", err) | ||
} else if err != nil && err.Error() != tc.expectedErr { | ||
t.Errorf("unexpected verification error: got %q, want %q", err.Error(), tc.expectedErr) | ||
} else if err == nil && tc.expectedErr != "" { | ||
t.Errorf("unexpected verification success: want %q", tc.expectedErr) | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters