diff --git a/component/ca/config.go b/component/ca/config.go index f50780af..2f582660 100644 --- a/component/ca/config.go +++ b/component/ca/config.go @@ -98,10 +98,13 @@ func GetTLSConfig(opt Option) (tlsConfig *tls.Config, err error) { } if len(opt.Fingerprint) > 0 { - tlsConfig.VerifyPeerCertificate, err = NewFingerprintVerifier(opt.Fingerprint, tlsConfig.Time) + verifier, err := NewFingerprintVerifier(opt.Fingerprint, tlsConfig.Time) if err != nil { return nil, err } + tlsConfig.VerifyConnection = func(state tls.ConnectionState) error { + return verifier(state.PeerCertificates, state.ServerName) + } tlsConfig.InsecureSkipVerify = true } diff --git a/component/ca/fingerprint.go b/component/ca/fingerprint.go index 3f240b1b..ceb05a4c 100644 --- a/component/ca/fingerprint.go +++ b/component/ca/fingerprint.go @@ -11,7 +11,7 @@ import ( ) // NewFingerprintVerifier returns a function that verifies whether a certificate's SHA-256 fingerprint matches the given one. -func NewFingerprintVerifier(fingerprint string, time func() time.Time) (func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error, error) { +func NewFingerprintVerifier(fingerprint string, time func() time.Time) (func(certs []*x509.Certificate, serverName string) error, error) { switch fingerprint { case "chrome", "firefox", "safari", "ios", "android", "edge", "360", "qq", "random", "randomized": // WTF??? return nil, fmt.Errorf("`fingerprint` is used for TLS certificate pinning. If you need to specify the browser fingerprint, use `client-fingerprint`") @@ -26,37 +26,24 @@ func NewFingerprintVerifier(fingerprint string, time func() time.Time) (func(raw return nil, fmt.Errorf("fingerprint string length error,need sha256 fingerprint") } - return func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { + return func(certs []*x509.Certificate, serverName string) error { // ssl pining - for i, rawCert := range rawCerts { - hash := sha256.Sum256(rawCert) + for i, cert := range certs { + hash := sha256.Sum256(cert.Raw) if bytes.Equal(fpByte, hash[:]) { if i > 0 { - // When the fingerprint matches a non-leaf certificate, // the certificate chain validity is verified using the certificate as the trusted root certificate. - // - // Currently, we do not verify that the SNI matches the certificate's DNS name, - // but we do verify the validity of the child certificate, - // including the issuance time and whether the child certificate was issued by the parent certificate. - - certs := make([]*x509.Certificate, i+1) // stop at i - for j := range certs { - cert, err := x509.ParseCertificate(rawCerts[j]) - if err != nil { - return err - } - certs[j] = cert - } opts := x509.VerifyOptions{ Roots: x509.NewCertPool(), Intermediates: x509.NewCertPool(), + DNSName: serverName, } if time != nil { opts.CurrentTime = time() } opts.Roots.AddCert(certs[i]) - for _, cert := range certs[1:] { + for _, cert := range certs[1 : i+1] { // stop at i opts.Intermediates.AddCert(cert) } _, err := certs[0].Verify(opts) diff --git a/component/ca/fingerprint_test.go b/component/ca/fingerprint_test.go index 6d8dff84..987a4357 100644 --- a/component/ca/fingerprint_test.go +++ b/component/ca/fingerprint_test.go @@ -1,6 +1,7 @@ package ca import ( + "crypto/x509" "encoding/pem" "testing" "time" @@ -10,90 +11,203 @@ import ( ) func TestFingerprintVerifierLeaf(t *testing.T) { - leafFingerprint := CalculateFingerprint(leafPEM.Bytes) - verifier, err := NewFingerprintVerifier(leafFingerprint, func() time.Time { - return time.Unix(1677615892, 0) - }) + leafFingerprint := CalculateFingerprint(leafCert.Raw) + verifier, err := NewFingerprintVerifier(leafFingerprint, certTime) require.NoError(t, err) - err = verifier([][]byte{leafPEM.Bytes, intermediatePEM.Bytes, rootPEM.Bytes}, nil) + err = verifier([]*x509.Certificate{leafCert, intermediateCert, rootCert}, "") assert.NoError(t, err) - err = verifier([][]byte{leafPEM.Bytes, smimeIntermediatePEM.Bytes, rootPEM.Bytes}, nil) + err = verifier([]*x509.Certificate{leafCert, intermediateCert, rootCert}, leafServerName) assert.NoError(t, err) - err = verifier([][]byte{leafPEM.Bytes, intermediatePEM.Bytes, smimeRootPEM.Bytes}, nil) + err = verifier([]*x509.Certificate{leafCert, intermediateCert, rootCert}, wrongLeafServerName) assert.NoError(t, err) - err = verifier([][]byte{leafWithInvalidHashPEM.Bytes, intermediatePEM.Bytes, rootPEM.Bytes}, nil) + err = verifier([]*x509.Certificate{leafCert, smimeIntermediateCert, rootCert}, "") + assert.NoError(t, err) + + err = verifier([]*x509.Certificate{leafCert, smimeIntermediateCert, rootCert}, leafServerName) + assert.NoError(t, err) + + err = verifier([]*x509.Certificate{leafCert, smimeIntermediateCert, rootCert}, wrongLeafServerName) + assert.NoError(t, err) + + err = verifier([]*x509.Certificate{leafCert, intermediateCert, smimeRootCert}, "") + assert.NoError(t, err) + + err = verifier([]*x509.Certificate{leafCert, intermediateCert, smimeRootCert}, leafServerName) + assert.NoError(t, err) + + err = verifier([]*x509.Certificate{leafCert, intermediateCert, smimeRootCert}, wrongLeafServerName) + assert.NoError(t, err) + + err = verifier([]*x509.Certificate{leafWithInvalidHashCert, intermediateCert, rootCert}, "") assert.Error(t, err) - err = verifier([][]byte{smimeLeafPEM.Bytes, intermediatePEM.Bytes, rootPEM.Bytes}, nil) + err = verifier([]*x509.Certificate{leafWithInvalidHashCert, intermediateCert, rootCert}, leafServerName) assert.Error(t, err) - err = verifier([][]byte{smimeLeafPEM.Bytes, intermediatePEM.Bytes, smimeRootPEM.Bytes}, nil) + err = verifier([]*x509.Certificate{leafWithInvalidHashCert, intermediateCert, rootCert}, wrongLeafServerName) + assert.Error(t, err) + + err = verifier([]*x509.Certificate{smimeLeafCert, intermediateCert, rootCert}, "") + assert.Error(t, err) + + err = verifier([]*x509.Certificate{smimeLeafCert, intermediateCert, rootCert}, leafServerName) + assert.Error(t, err) + + err = verifier([]*x509.Certificate{smimeLeafCert, intermediateCert, rootCert}, wrongLeafServerName) + assert.Error(t, err) + + err = verifier([]*x509.Certificate{smimeLeafCert, intermediateCert, smimeRootCert}, "") + assert.Error(t, err) + + err = verifier([]*x509.Certificate{smimeLeafCert, intermediateCert, smimeRootCert}, leafServerName) + assert.Error(t, err) + + err = verifier([]*x509.Certificate{smimeLeafCert, intermediateCert, smimeRootCert}, wrongLeafServerName) assert.Error(t, err) } func TestFingerprintVerifierIntermediate(t *testing.T) { - intermediateFingerprint := CalculateFingerprint(intermediatePEM.Bytes) - verifier, err := NewFingerprintVerifier(intermediateFingerprint, func() time.Time { - return time.Unix(1677615892, 0) - }) + intermediateFingerprint := CalculateFingerprint(intermediateCert.Raw) + verifier, err := NewFingerprintVerifier(intermediateFingerprint, certTime) require.NoError(t, err) - err = verifier([][]byte{leafPEM.Bytes, intermediatePEM.Bytes, rootPEM.Bytes}, nil) + err = verifier([]*x509.Certificate{leafCert, intermediateCert, rootCert}, "") assert.NoError(t, err) - err = verifier([][]byte{leafPEM.Bytes, smimeIntermediatePEM.Bytes, rootPEM.Bytes}, nil) - assert.Error(t, err) - - err = verifier([][]byte{leafPEM.Bytes, intermediatePEM.Bytes, smimeRootPEM.Bytes}, nil) + err = verifier([]*x509.Certificate{leafCert, intermediateCert, rootCert}, leafServerName) assert.NoError(t, err) - err = verifier([][]byte{leafWithInvalidHashPEM.Bytes, intermediatePEM.Bytes, rootPEM.Bytes}, nil) + err = verifier([]*x509.Certificate{leafCert, intermediateCert, rootCert}, wrongLeafServerName) assert.Error(t, err) - err = verifier([][]byte{smimeLeafPEM.Bytes, intermediatePEM.Bytes, rootPEM.Bytes}, nil) + err = verifier([]*x509.Certificate{leafCert, smimeIntermediateCert, rootCert}, "") assert.Error(t, err) - err = verifier([][]byte{smimeLeafPEM.Bytes, intermediatePEM.Bytes, smimeRootPEM.Bytes}, nil) + err = verifier([]*x509.Certificate{leafCert, smimeIntermediateCert, rootCert}, leafServerName) + assert.Error(t, err) + + err = verifier([]*x509.Certificate{leafCert, smimeIntermediateCert, rootCert}, wrongLeafServerName) + assert.Error(t, err) + + err = verifier([]*x509.Certificate{leafCert, intermediateCert, smimeRootCert}, "") + assert.NoError(t, err) + + err = verifier([]*x509.Certificate{leafCert, intermediateCert, smimeRootCert}, leafServerName) + assert.NoError(t, err) + + err = verifier([]*x509.Certificate{leafCert, intermediateCert, smimeRootCert}, wrongLeafServerName) + assert.Error(t, err) + + err = verifier([]*x509.Certificate{leafWithInvalidHashCert, intermediateCert, rootCert}, "") + assert.Error(t, err) + + err = verifier([]*x509.Certificate{leafWithInvalidHashCert, intermediateCert, rootCert}, leafServerName) + assert.Error(t, err) + + err = verifier([]*x509.Certificate{leafWithInvalidHashCert, intermediateCert, rootCert}, wrongLeafServerName) + assert.Error(t, err) + + err = verifier([]*x509.Certificate{smimeLeafCert, intermediateCert, rootCert}, "") + assert.Error(t, err) + + err = verifier([]*x509.Certificate{smimeLeafCert, intermediateCert, rootCert}, leafServerName) + assert.Error(t, err) + + err = verifier([]*x509.Certificate{smimeLeafCert, intermediateCert, rootCert}, wrongLeafServerName) + assert.Error(t, err) + + err = verifier([]*x509.Certificate{smimeLeafCert, intermediateCert, smimeRootCert}, "") + assert.Error(t, err) + + err = verifier([]*x509.Certificate{smimeLeafCert, intermediateCert, smimeRootCert}, leafServerName) + assert.Error(t, err) + + err = verifier([]*x509.Certificate{smimeLeafCert, intermediateCert, smimeRootCert}, wrongLeafServerName) assert.Error(t, err) } func TestFingerprintVerifierRoot(t *testing.T) { - rootFingerprint := CalculateFingerprint(rootPEM.Bytes) - verifier, err := NewFingerprintVerifier(rootFingerprint, func() time.Time { - return time.Unix(1677615892, 0) - }) + rootFingerprint := CalculateFingerprint(rootCert.Raw) + verifier, err := NewFingerprintVerifier(rootFingerprint, certTime) require.NoError(t, err) - err = verifier([][]byte{leafPEM.Bytes, intermediatePEM.Bytes, rootPEM.Bytes}, nil) + err = verifier([]*x509.Certificate{leafCert, intermediateCert, rootCert}, "") assert.NoError(t, err) - err = verifier([][]byte{leafPEM.Bytes, smimeIntermediatePEM.Bytes, rootPEM.Bytes}, nil) + err = verifier([]*x509.Certificate{leafCert, intermediateCert, rootCert}, leafServerName) + assert.NoError(t, err) + + err = verifier([]*x509.Certificate{leafCert, intermediateCert, rootCert}, wrongLeafServerName) assert.Error(t, err) - err = verifier([][]byte{leafPEM.Bytes, intermediatePEM.Bytes, smimeRootPEM.Bytes}, nil) + err = verifier([]*x509.Certificate{leafCert, smimeIntermediateCert, rootCert}, "") assert.Error(t, err) - err = verifier([][]byte{leafWithInvalidHashPEM.Bytes, intermediatePEM.Bytes, rootPEM.Bytes}, nil) + err = verifier([]*x509.Certificate{leafCert, smimeIntermediateCert, rootCert}, leafServerName) assert.Error(t, err) - err = verifier([][]byte{smimeLeafPEM.Bytes, intermediatePEM.Bytes, rootPEM.Bytes}, nil) + err = verifier([]*x509.Certificate{leafCert, smimeIntermediateCert, rootCert}, wrongLeafServerName) assert.Error(t, err) - err = verifier([][]byte{smimeLeafPEM.Bytes, intermediatePEM.Bytes, smimeRootPEM.Bytes}, nil) + err = verifier([]*x509.Certificate{leafCert, intermediateCert, smimeRootCert}, "") + assert.Error(t, err) + + err = verifier([]*x509.Certificate{leafCert, intermediateCert, smimeRootCert}, leafServerName) + assert.Error(t, err) + + err = verifier([]*x509.Certificate{leafCert, intermediateCert, smimeRootCert}, wrongLeafServerName) + assert.Error(t, err) + + err = verifier([]*x509.Certificate{leafWithInvalidHashCert, intermediateCert, rootCert}, "") + assert.Error(t, err) + + err = verifier([]*x509.Certificate{leafWithInvalidHashCert, intermediateCert, rootCert}, leafServerName) + assert.Error(t, err) + + err = verifier([]*x509.Certificate{leafWithInvalidHashCert, intermediateCert, rootCert}, wrongLeafServerName) + assert.Error(t, err) + + err = verifier([]*x509.Certificate{smimeLeafCert, intermediateCert, rootCert}, "") + assert.Error(t, err) + + err = verifier([]*x509.Certificate{smimeLeafCert, intermediateCert, rootCert}, leafServerName) + assert.Error(t, err) + + err = verifier([]*x509.Certificate{smimeLeafCert, intermediateCert, rootCert}, wrongLeafServerName) + assert.Error(t, err) + + err = verifier([]*x509.Certificate{smimeLeafCert, intermediateCert, smimeRootCert}, "") + assert.Error(t, err) + + err = verifier([]*x509.Certificate{smimeLeafCert, intermediateCert, smimeRootCert}, leafServerName) + assert.Error(t, err) + + err = verifier([]*x509.Certificate{smimeLeafCert, intermediateCert, smimeRootCert}, wrongLeafServerName) assert.Error(t, err) } var rootPEM, _ = pem.Decode([]byte(gtsRoot)) +var rootCert, _ = x509.ParseCertificate(rootPEM.Bytes) var intermediatePEM, _ = pem.Decode([]byte(gtsIntermediate)) +var intermediateCert, _ = x509.ParseCertificate(intermediatePEM.Bytes) var leafPEM, _ = pem.Decode([]byte(googleLeaf)) +var leafCert, _ = x509.ParseCertificate(leafPEM.Bytes) var leafWithInvalidHashPEM, _ = pem.Decode([]byte(googleLeafWithInvalidHash)) +var leafWithInvalidHashCert, _ = x509.ParseCertificate(leafWithInvalidHashPEM.Bytes) var smimeRootPEM, _ = pem.Decode([]byte(smimeRoot)) +var smimeRootCert, _ = x509.ParseCertificate(smimeRootPEM.Bytes) var smimeIntermediatePEM, _ = pem.Decode([]byte(smimeIntermediate)) +var smimeIntermediateCert, _ = x509.ParseCertificate(smimeIntermediatePEM.Bytes) var smimeLeafPEM, _ = pem.Decode([]byte(smimeLeaf)) +var smimeLeafCert, _ = x509.ParseCertificate(smimeLeafPEM.Bytes) +var certTime = func() time.Time { return time.Unix(1677615892, 0) } + +const leafServerName = "www.google.com" +const wrongLeafServerName = "www.google.com.cn" const gtsIntermediate = `-----BEGIN CERTIFICATE----- MIIFljCCA36gAwIBAgINAgO8U1lrNMcY9QFQZjANBgkqhkiG9w0BAQsFADBHMQsw