chore: fingerprint verifier handle non-leaf certificate will check the SNI matches the certificate's DNS name

This commit is contained in:
wwqgtxx
2026-02-04 22:41:33 +08:00
parent 2cfc4ba044
commit f94da9f2b3
3 changed files with 156 additions and 52 deletions

View File

@@ -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
}

View File

@@ -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)

View File

@@ -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