// Package ssl 提供 mTLS 客户端证书验证支持。 // // 该文件包含客户端证书验证的核心逻辑,包括: // - CA 证书池加载和管理 // - 证书吊销列表 (CRL) 支持 // - 验证模式配置 // - 客户端证书信息提取 // // mTLS (Mutual TLS) 提供双向认证,服务器验证客户端证书, // 客户端验证服务器证书,适用于高安全场景。 // // 作者:xfy package ssl import ( "crypto/tls" "crypto/x509" "encoding/pem" "errors" "fmt" "os" "time" "rua.plus/lolly/internal/config" "rua.plus/lolly/internal/sslutil" ) // ClientVerifyMode 客户端证书验证模式 type ClientVerifyMode int const ( // VerifyOff 不验证客户端证书。 VerifyOff ClientVerifyMode = iota // VerifyOn 强制验证客户端证书。 VerifyOn // VerifyOptional 可选验证(客户端可选择不提供证书)。 VerifyOptional // VerifyOptionalNoCA 可选验证但不验证 CA。 VerifyOptionalNoCA // verifyModeOff 验证模式字符串常量 verifyModeOff = "off" verifyModeOn = "on" verifyModeOptional = "optional" verifyModeOptionalNoCA = "optional_no_ca" ) // ParseVerifyMode 解析验证模式字符串。 // // 参数: // - mode: 模式字符串(on/off/optional/optional_no_ca) // // 返回值: // - ClientVerifyMode: 验证模式 // - error: 无效模式时返回错误 func ParseVerifyMode(mode string) (ClientVerifyMode, error) { switch mode { case verifyModeOff, "": return VerifyOff, nil case verifyModeOn: return VerifyOn, nil case verifyModeOptional: return VerifyOptional, nil case verifyModeOptionalNoCA: return VerifyOptionalNoCA, nil default: return VerifyOff, fmt.Errorf("invalid verify mode: %s", mode) } } // TLSClientAuth 返回对应的 tls.ClientAuthType。 // // 返回值: // - tls.ClientAuthType: TLS 客户端认证类型 func (m ClientVerifyMode) TLSClientAuth() tls.ClientAuthType { switch m { case VerifyOff: return tls.NoClientCert case VerifyOn: return tls.RequireAndVerifyClientCert case VerifyOptional: return tls.VerifyClientCertIfGiven case VerifyOptionalNoCA: return tls.RequestClientCert default: return tls.NoClientCert } } // ClientVerifier 客户端证书验证器。 // // 管理客户端证书验证所需的 CA 证书池和 CRL。 type ClientVerifier struct { caPool *x509.CertPool crl *x509.RevocationList caFile string crlFile string mode ClientVerifyMode verifyDepth int } // NewClientVerifier 创建新的客户端证书验证器。 // // 参数: // - cfg: 客户端验证配置 // // 返回值: // - *ClientVerifier: 验证器实例 // - error: 配置无效时返回错误 func NewClientVerifier(cfg config.ClientVerifyConfig) (*ClientVerifier, error) { if !cfg.Enabled { return &ClientVerifier{ mode: VerifyOff, }, nil } mode, err := ParseVerifyMode(cfg.Mode) if err != nil { return nil, err } verifier := &ClientVerifier{ mode: mode, verifyDepth: cfg.VerifyDepth, caFile: cfg.ClientCA, crlFile: cfg.CRL, } // 加载 CA 证书池(如果需要验证) if mode == VerifyOn || mode == VerifyOptional { if cfg.ClientCA == "" { return nil, errors.New("client_ca is required when verify is enabled") } caPool, err := sslutil.LoadCACertPool(cfg.ClientCA) if err != nil { return nil, fmt.Errorf("failed to load CA certificate pool: %w", err) } verifier.caPool = caPool } // 加载 CRL(如果配置) if cfg.CRL != "" { crl, err := LoadCRL(cfg.CRL) if err != nil { return nil, fmt.Errorf("failed to load CRL: %w", err) } verifier.crl = crl } return verifier, nil } // ConfigureTLS 配置 TLS 以启用客户端证书验证。 // // 参数: // - tlsCfg: TLS 配置对象 func (v *ClientVerifier) ConfigureTLS(tlsCfg *tls.Config) { if tlsCfg == nil || v.mode == VerifyOff { return } tlsCfg.ClientAuth = v.mode.TLSClientAuth() tlsCfg.ClientCAs = v.caPool // 设置验证深度(通过 VerifyConnection 回调实现) if v.verifyDepth > 0 { tlsCfg.VerifyConnection = v.verifyConnection } } // verifyConnection 验证 TLS 连接。 // // 实现额外的验证逻辑,如证书深度检查。 // // 参数: // - cs: 连接状态 // // 返回值: // - error: 验证失败时返回错误 func (v *ClientVerifier) verifyConnection(cs tls.ConnectionState) error { // 检查 CRL if v.crl != nil && len(cs.PeerCertificates) > 0 { if err := v.checkCRL(cs.PeerCertificates[0]); err != nil { return err } } // 检查证书链深度 if v.verifyDepth > 0 && len(cs.PeerCertificates) > v.verifyDepth { return fmt.Errorf("certificate chain too long: %d > %d", len(cs.PeerCertificates), v.verifyDepth) } return nil } // checkCRL 检查证书是否在吊销列表中。 // // 参数: // - cert: 要检查的证书 // // 返回值: // - error: 证书已吊销时返回错误 func (v *ClientVerifier) checkCRL(cert *x509.Certificate) error { if v.crl == nil || len(v.crl.RevokedCertificateEntries) == 0 { return nil } for _, revoked := range v.crl.RevokedCertificateEntries { if cert.SerialNumber.Cmp(revoked.SerialNumber) == 0 { return fmt.Errorf("certificate %s has been revoked", cert.SerialNumber.String()) } } return nil } // LoadCRL 从文件加载证书吊销列表。 // // 支持 PEM 和 DER 格式的 CRL 文件。 // // 参数: // - crlFile: CRL 文件路径 // // 返回值: // - *pkix.CertificateList: CRL 对象 // - error: 加载失败时返回错误 func LoadCRL(crlFile string) (*x509.RevocationList, error) { data, err := os.ReadFile(crlFile) if err != nil { return nil, fmt.Errorf("failed to read CRL file: %w", err) } // 尝试 PEM 解码 block, _ := pem.Decode(data) if block != nil { data = block.Bytes } crl, err := x509.ParseRevocationList(data) if err != nil { return nil, fmt.Errorf("failed to parse CRL: %w", err) } return crl, nil } // ClientCertInfo 客户端证书信息。 type ClientCertInfo struct { NotBefore time.Time NotAfter time.Time Subject string Issuer string Serial string Fingerprint string DNSNames []string Email []string }