mirror of
https://github.com/XTLS/Xray-core.git
synced 2026-07-14 08:58:47 +00:00
TLS client: Skip TLS' built-in verification when using pinnedPeerCertSha256; Fixes
https://github.com/XTLS/Xray-core/pull/5532#issuecomment-3745598515 https://github.com/XTLS/Xray-core/pull/5532#issuecomment-3759930283 https://github.com/XTLS/Xray-core/pull/5532#issuecomment-3760057266 https://github.com/XTLS/Xray-core/pull/5532#issuecomment-3760540231
This commit is contained in:
@@ -281,35 +281,41 @@ func (c *Config) parseServerName() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *RandCarrier) verifyPeerCert(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) (err error) {
|
func (r *RandCarrier) verifyPeerCert(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) (err error) {
|
||||||
// extract x509 certificates from rawCerts(verifiedChains will be nil if InsecureSkipVerify is true)
|
// extract x509 certificates from rawCerts (verifiedChains will be nil if InsecureSkipVerify is true)
|
||||||
certs := make([]*x509.Certificate, len(rawCerts))
|
certs := make([]*x509.Certificate, len(rawCerts))
|
||||||
for i, asn1Data := range rawCerts {
|
for i, asn1Data := range rawCerts {
|
||||||
certs[i], _ = x509.ParseCertificate(asn1Data)
|
certs[i], _ = x509.ParseCertificate(asn1Data)
|
||||||
}
|
}
|
||||||
|
if len(certs) == 0 {
|
||||||
|
return errors.New("unexpected certs")
|
||||||
|
}
|
||||||
|
if certs[0].IsCA {
|
||||||
|
slices.Reverse(certs)
|
||||||
|
}
|
||||||
|
|
||||||
// directly return success if pinned cert is leaf
|
// directly return success if pinned cert is leaf
|
||||||
// or add the CA to RootCAs if pinned cert is CA(and can be used in VerifyPeerCertInNames for Self signed CA)
|
// or replace RootCAs if pinned cert is CA (and can be used in VerifyPeerCertInNames)
|
||||||
RootCAs := r.RootCAs
|
CAs := r.RootCAs
|
||||||
var verifyResult verifyResult
|
var verifyResult verifyResult
|
||||||
var verifiedCert *x509.Certificate
|
var verifiedCert *x509.Certificate
|
||||||
if r.PinnedPeerCertSha256 != nil {
|
if r.PinnedPeerCertSha256 != nil {
|
||||||
verifyResult, verifiedCert = verifyChain(certs, r.PinnedPeerCertSha256)
|
verifyResult, verifiedCert = verifyChain(certs, r.PinnedPeerCertSha256)
|
||||||
switch verifyResult {
|
switch verifyResult {
|
||||||
case certNotFound:
|
case certNotFound:
|
||||||
return errors.New("peer cert is unrecognized")
|
return errors.New("peer cert is unrecognized (againsts pinnedPeerCertSha256)")
|
||||||
case foundLeaf:
|
case foundLeaf:
|
||||||
return nil
|
return nil
|
||||||
case foundCA:
|
case foundCA:
|
||||||
RootCAs = x509.NewCertPool()
|
CAs = x509.NewCertPool()
|
||||||
RootCAs.AddCert(verifiedCert)
|
CAs.AddCert(verifiedCert)
|
||||||
default:
|
default:
|
||||||
panic("impossible PinnedPeerCertificateSha256 verify result")
|
panic("impossible pinnedPeerCertSha256 verify result")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(r.VerifyPeerCertInNames) > 0 {
|
if r.VerifyPeerCertInNames != nil { // RAW's Dial() may make it empty but not nil
|
||||||
opts := x509.VerifyOptions{
|
opts := x509.VerifyOptions{
|
||||||
Roots: RootCAs,
|
Roots: CAs,
|
||||||
CurrentTime: time.Now(),
|
CurrentTime: time.Now(),
|
||||||
Intermediates: x509.NewCertPool(),
|
Intermediates: x509.NewCertPool(),
|
||||||
}
|
}
|
||||||
@@ -321,9 +327,15 @@ func (r *RandCarrier) verifyPeerCert(rawCerts [][]byte, verifiedChains [][]*x509
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if len(verifiedChains) == 0 && verifyResult == foundCA { // if found ca and verifiedChains is empty, we need to verify here
|
if verifyResult == foundCA {
|
||||||
|
errors.New("peer cert is invalid (againsts pinned CA and verifyPeerCertInNames)")
|
||||||
|
}
|
||||||
|
return errors.New("peer cert is invalid (againsts root CAs and verifyPeerCertInNames)")
|
||||||
|
}
|
||||||
|
|
||||||
|
if verifyResult == foundCA { // if found CA, we need to verify here
|
||||||
opts := x509.VerifyOptions{
|
opts := x509.VerifyOptions{
|
||||||
Roots: RootCAs,
|
Roots: CAs,
|
||||||
CurrentTime: time.Now(),
|
CurrentTime: time.Now(),
|
||||||
Intermediates: x509.NewCertPool(),
|
Intermediates: x509.NewCertPool(),
|
||||||
DNSName: r.Config.ServerName,
|
DNSName: r.Config.ServerName,
|
||||||
@@ -334,8 +346,10 @@ func (r *RandCarrier) verifyPeerCert(rawCerts [][]byte, verifiedChains [][]*x509
|
|||||||
if _, err := certs[0].Verify(opts); err == nil {
|
if _, err := certs[0].Verify(opts); err == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
return errors.New("peer cert is invalid (againsts pinned CA and serverName)")
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
|
return nil // len(r.PinnedPeerCertSha256)==nil && len(r.VerifyPeerCertInNames)==nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type RandCarrier struct {
|
type RandCarrier struct {
|
||||||
@@ -386,6 +400,11 @@ func (c *Config) GetTLSConfig(opts ...Option) *tls.Config {
|
|||||||
} else {
|
} else {
|
||||||
randCarrier.VerifyPeerCertInNames = nil
|
randCarrier.VerifyPeerCertInNames = nil
|
||||||
}
|
}
|
||||||
|
if len(c.PinnedPeerCertSha256) > 0 {
|
||||||
|
config.InsecureSkipVerify = true
|
||||||
|
} else {
|
||||||
|
randCarrier.PinnedPeerCertSha256 = nil
|
||||||
|
}
|
||||||
|
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
opt(config)
|
opt(config)
|
||||||
@@ -540,17 +559,16 @@ const (
|
|||||||
foundCA
|
foundCA
|
||||||
)
|
)
|
||||||
|
|
||||||
func verifyChain(certs []*x509.Certificate, PinnedPeerCertificateSha256 [][]byte) (verifyResult, *x509.Certificate) {
|
func verifyChain(certs []*x509.Certificate, pinnedPeerCertSha256 [][]byte) (verifyResult, *x509.Certificate) {
|
||||||
for _, cert := range certs {
|
for _, cert := range certs {
|
||||||
certHash := GenerateCertHash(cert)
|
certHash := GenerateCertHash(cert)
|
||||||
for _, c := range PinnedPeerCertificateSha256 {
|
for _, c := range pinnedPeerCertSha256 {
|
||||||
if hmac.Equal(certHash, c) {
|
if hmac.Equal(certHash, c) {
|
||||||
if cert.IsCA {
|
if cert.IsCA {
|
||||||
return foundCA, cert
|
return foundCA, cert
|
||||||
} else {
|
} else {
|
||||||
return foundLeaf, cert
|
return foundLeaf, cert
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user