diff --git a/internal/proxy/proxy_ssl.go b/internal/proxy/proxy_ssl.go index 2efb824..f4cab5a 100644 --- a/internal/proxy/proxy_ssl.go +++ b/internal/proxy/proxy_ssl.go @@ -31,20 +31,9 @@ import ( "rua.plus/lolly/internal/config" "rua.plus/lolly/internal/logging" + "rua.plus/lolly/internal/sslutil" ) -// tlsVersionMap TLS 版本字符串到 tls 常量的映射表。 -// -// 支持 TLSv1.0、TLSv1.1、TLSv1.2、TLSv1.3 格式(大小写不敏感)。 -// 空字符串表示使用 Go 标准库默认值。 -var tlsVersionMap = map[string]uint16{ - "TLSV1.0": tls.VersionTLS10, - "TLSV1.1": tls.VersionTLS11, - "TLSV1.2": tls.VersionTLS12, - "TLSV1.3": tls.VersionTLS13, - "": 0, // 空字符串表示使用默认 -} - // CreateTLSConfig 从 ProxySSLConfig 创建 tls.Config。 // // 参数: @@ -105,7 +94,7 @@ func CreateTLSConfig(cfg *config.ProxySSLConfig, defaultServerName string) (*tls tlsCfg.MinVersion = tls.VersionTLS12 if cfg.MinVersion != "" { - version, ok := tlsVersionMap[strings.ToUpper(cfg.MinVersion)] + version, ok := sslutil.TLSVersionMap[strings.ToUpper(cfg.MinVersion)] if !ok { return nil, errors.New("invalid TLS min version: " + cfg.MinVersion) } @@ -118,7 +107,7 @@ func CreateTLSConfig(cfg *config.ProxySSLConfig, defaultServerName string) (*tls } if cfg.MaxVersion != "" { - version, ok := tlsVersionMap[strings.ToUpper(cfg.MaxVersion)] + version, ok := sslutil.TLSVersionMap[strings.ToUpper(cfg.MaxVersion)] if !ok { return nil, errors.New("invalid TLS max version: " + cfg.MaxVersion) } diff --git a/internal/ssl/ssl.go b/internal/ssl/ssl.go index 79b82bd..226ad2a 100644 --- a/internal/ssl/ssl.go +++ b/internal/ssl/ssl.go @@ -43,12 +43,12 @@ import ( "errors" "fmt" "os" - "slices" "sync" "rua.plus/lolly/internal/config" "rua.plus/lolly/internal/logging" "rua.plus/lolly/internal/netutil" + "rua.plus/lolly/internal/sslutil" ) // TLSManager TLS 配置管理器。 @@ -116,19 +116,19 @@ func NewTLSManager(cfg *config.SSLConfig) (*TLSManager, error) { // 应用 TLS 1.2 的加密套件 if len(cfg.Ciphers) > 0 { - ciphers, err := parseCipherSuites(cfg.Ciphers) + ciphers, err := sslutil.ParseCipherSuites(cfg.Ciphers) if err != nil { return nil, fmt.Errorf("invalid cipher suites: %w", err) } tlsCfg.CipherSuites = ciphers } else { // 使用安全的默认加密套件 - tlsCfg.CipherSuites = defaultCipherSuites() + tlsCfg.CipherSuites = sslutil.DefaultCipherSuites() } // 解析 TLS 协议版本 if len(cfg.Protocols) > 0 { - minVer, maxVer, err := parseTLSVersions(cfg.Protocols) + minVer, maxVer, err := sslutil.ParseTLSVersions(cfg.Protocols) if err != nil { return nil, fmt.Errorf("invalid TLS protocols: %w", err) } @@ -590,17 +590,17 @@ func createTLSConfig(cfg *config.SSLConfig) (*tls.Config, error) { } if len(cfg.Ciphers) > 0 { - ciphers, err := parseCipherSuites(cfg.Ciphers) + ciphers, err := sslutil.ParseCipherSuites(cfg.Ciphers) if err != nil { return nil, err } tlsCfg.CipherSuites = ciphers } else { - tlsCfg.CipherSuites = defaultCipherSuites() + tlsCfg.CipherSuites = sslutil.DefaultCipherSuites() } if len(cfg.Protocols) > 0 { - minVer, maxVer, err := parseTLSVersions(cfg.Protocols) + minVer, maxVer, err := sslutil.ParseTLSVersions(cfg.Protocols) if err != nil { return nil, err } @@ -611,123 +611,6 @@ func createTLSConfig(cfg *config.SSLConfig) (*tls.Config, error) { return tlsCfg, nil } -// parseTLSVersions 解析 TLS 协议版本字符串。 -// -// 返回最小和最大 TLS 版本。 -// -// 参数: -// - protocols: 协议名称列表(如 "TLSv1.2", "TLSv1.3") -// -// 返回值: -// - uint16: 最小版本 -// - uint16: 最大版本 -// - error: 无效协议时返回错误 -func parseTLSVersions(protocols []string) (uint16, uint16, error) { - var minVer, maxVer uint16 - minVer = tls.VersionTLS13 // 默认最高版本 - maxVer = tls.VersionTLS13 - - for _, p := range protocols { - switch p { - case "TLSv1.2": - if minVer > tls.VersionTLS12 { - minVer = tls.VersionTLS12 - } - case "TLSv1.3": - maxVer = tls.VersionTLS13 - case "TLSv1.0", "TLSv1.1": - return 0, 0, fmt.Errorf("insecure TLS version %s is not supported", p) - default: - return 0, 0, fmt.Errorf("unknown TLS version: %s", p) - } - } - - return minVer, maxVer, nil -} - -// parseCipherSuites 解析加密套件名称字符串为 TLS ID。 -// -// 参数: -// - ciphers: 加密套件名称列表 -// -// 返回值: -// - []uint16: 加密套件 ID 列表 -// - error: 未知或不安全的加密套件时返回错误 -func parseCipherSuites(ciphers []string) ([]uint16, error) { - result := make([]uint16, 0, len(ciphers)) - - for _, c := range ciphers { - id, ok := cipherSuiteMap[c] - if !ok { - return nil, fmt.Errorf("unknown cipher suite: %s", c) - } - // 检查不安全的加密套件 - if isInsecureCipher(id) { - return nil, fmt.Errorf("insecure cipher suite %s is not allowed", c) - } - result = append(result, id) - } - - return result, nil -} - -// isInsecureCipher 检查加密套件是否不安全。 -// -// 参数: -// - id: 加密套件 ID -// -// 返回值: -// - bool: 不安全返回 true -func isInsecureCipher(id uint16) bool { - insecureCiphers := []uint16{ - tls.TLS_RSA_WITH_RC4_128_SHA, - tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA, - tls.TLS_RSA_WITH_AES_128_CBC_SHA256, - tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, - tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA, - tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, - tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, - tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, - } - - return slices.Contains(insecureCiphers, id) -} - -// defaultCipherSuites 返回 TLS 1.2 推荐的加密套件。 -// -// 优先选择前向保密和 AEAD 加密算法。 -// -// 返回值: -// - []uint16: 加密套件 ID 列表 -func defaultCipherSuites() []uint16 { - return []uint16{ - tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, - tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, - } -} - -// cipherSuiteMap 加密套件名称到 TLS ID 的映射。 -var cipherSuiteMap = map[string]uint16{ - "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, - "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, - "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, - "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, - "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, - "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, - "TLS_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_RSA_WITH_AES_128_GCM_SHA256, - "TLS_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_RSA_WITH_AES_256_GCM_SHA384, - "TLS_RSA_WITH_AES_128_CBC_SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA, - "TLS_RSA_WITH_AES_256_CBC_SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA, -} - // ValidateCertificate 验证证书文件。 // // 检查证书是否有效且未过期。 diff --git a/internal/ssl/ssl_bench_test.go b/internal/ssl/ssl_bench_test.go index 99327ab..3aad627 100644 --- a/internal/ssl/ssl_bench_test.go +++ b/internal/ssl/ssl_bench_test.go @@ -17,6 +17,7 @@ import ( "time" "rua.plus/lolly/internal/config" + "rua.plus/lolly/internal/sslutil" ) // BenchmarkTLSHandshake 基准测试 TLS 握手性能。 @@ -660,7 +661,7 @@ func BenchmarkCipherSuiteParsing(b *testing.B) { b.ResetTimer() for b.Loop() { - _, err := parseCipherSuites(ciphers) + _, err := sslutil.ParseCipherSuites(ciphers) if err != nil { b.Fatal(err) } @@ -673,7 +674,7 @@ func BenchmarkTLSVersionsParsing(b *testing.B) { b.ResetTimer() for b.Loop() { - _, _, err := parseTLSVersions(protocols) + _, _, err := sslutil.ParseTLSVersions(protocols) if err != nil { b.Fatal(err) } diff --git a/internal/ssl/ssl_test.go b/internal/ssl/ssl_test.go index 6ce71a1..6799d4c 100644 --- a/internal/ssl/ssl_test.go +++ b/internal/ssl/ssl_test.go @@ -25,6 +25,7 @@ import ( "time" "rua.plus/lolly/internal/config" + "rua.plus/lolly/internal/sslutil" ) func TestNewTLSManager(t *testing.T) { @@ -172,7 +173,7 @@ func TestParseTLSVersions(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - minVer, maxVer, err := parseTLSVersions(tt.protocols) + minVer, maxVer, err := sslutil.ParseTLSVersions(tt.protocols) if (err != nil) != tt.wantErr { t.Errorf("parseTLSVersions() error = %v, wantErr %v", err, tt.wantErr) return @@ -218,7 +219,7 @@ func TestParseCipherSuites(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - result, err := parseCipherSuites(tt.ciphers) + result, err := sslutil.ParseCipherSuites(tt.ciphers) if (err != nil) != tt.wantErr { t.Errorf("parseCipherSuites() error = %v, wantErr %v", err, tt.wantErr) return @@ -231,14 +232,14 @@ func TestParseCipherSuites(t *testing.T) { } func TestDefaultCipherSuites(t *testing.T) { - suites := defaultCipherSuites() + suites := sslutil.DefaultCipherSuites() if len(suites) == 0 { t.Error("Expected non-empty default cipher suites") } // Check that all default ciphers are secure for _, suite := range suites { - if isInsecureCipher(suite) { + if sslutil.IsInsecureCipher(suite) { t.Errorf("Default cipher suite %v is insecure", suite) } } @@ -252,7 +253,7 @@ func TestIsInsecureCipher(t *testing.T) { } for _, c := range insecureCiphers { - if !isInsecureCipher(c) { + if !sslutil.IsInsecureCipher(c) { t.Errorf("Expected cipher %v to be insecure", c) } } @@ -264,7 +265,7 @@ func TestIsInsecureCipher(t *testing.T) { } for _, c := range secureCiphers { - if isInsecureCipher(c) { + if sslutil.IsInsecureCipher(c) { t.Errorf("Expected cipher %v to be secure", c) } } diff --git a/internal/sslutil/tlsconfig.go b/internal/sslutil/tlsconfig.go new file mode 100644 index 0000000..6313ebb --- /dev/null +++ b/internal/sslutil/tlsconfig.go @@ -0,0 +1,224 @@ +// Package sslutil provides SSL/TLS utility functions. +package sslutil + +import ( + "crypto/tls" + "fmt" + "slices" +) + +// ParseTLSVersion parses a TLS version string to a tls constant. +// +// Parameters: +// - version: TLS version string (e.g., "TLSv1.0", "TLSv1.1", "TLSv1.2", "TLSv1.3") +// +// Returns: +// - uint16: TLS version constant +// - error: Invalid version string returns error +func ParseTLSVersion(version string) (uint16, error) { + switch version { + case "TLSv1.0", "TLSV1.0": + return tls.VersionTLS10, nil + case "TLSv1.1", "TLSV1.1": + return tls.VersionTLS11, nil + case "TLSv1.2", "TLSV1.2": + return tls.VersionTLS12, nil + case "TLSv1.3", "TLSV1.3": + return tls.VersionTLS13, nil + case "": + return 0, nil // Empty string means use default + default: + return 0, fmt.Errorf("invalid TLS version: %s", version) + } +} + +// ParseTLSVersions parses TLS protocol version strings. +// +// Returns minimum and maximum TLS versions. +// +// Parameters: +// - protocols: Protocol name list (e.g., "TLSv1.2", "TLSv1.3") +// +// Returns: +// - uint16: Minimum version +// - uint16: Maximum version +// - error: Invalid protocol returns error +func ParseTLSVersions(protocols []string) (uint16, uint16, error) { + var minVer, maxVer uint16 + minVer = tls.VersionTLS13 // Default to highest version + maxVer = tls.VersionTLS13 + + for _, p := range protocols { + switch p { + case "TLSv1.2", "TLSV1.2": + if minVer > tls.VersionTLS12 { + minVer = tls.VersionTLS12 + } + case "TLSv1.3", "TLSV1.3": + maxVer = tls.VersionTLS13 + case "TLSv1.0", "TLSV1.0", "TLSv1.1", "TLSV1.1": + return 0, 0, fmt.Errorf("insecure TLS version %s is not supported", p) + default: + return 0, 0, fmt.Errorf("unknown TLS version: %s", p) + } + } + + return minVer, maxVer, nil +} + +// ParseMinTLSVersion parses the minimum TLS version from protocol list. +// +// Parameters: +// - protocols: Protocol version list +// +// Returns: +// - uint16: TLS version constant +func ParseMinTLSVersion(protocols []string) uint16 { + for _, p := range protocols { + switch p { + case "TLSv1.3", "TLSV1.3": + return tls.VersionTLS13 + case "TLSv1.2", "TLSV1.2": + return tls.VersionTLS12 + } + } + return tls.VersionTLS12 +} + +// cipherNameToID maps cipher suite names to TLS IDs. +// Supports both OpenSSL-style names and Go standard names. +var cipherNameToID = map[string]uint16{ + // OpenSSL-style names (ECDHE-RSA-AES128-GCM-SHA256) + "ECDHE-RSA-AES128-GCM-SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + "ECDHE-RSA-AES256-GCM-SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + "ECDHE-ECDSA-AES128-GCM-SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + "ECDHE-ECDSA-AES256-GCM-SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + "ECDHE-RSA-CHACHA20-POLY1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + "ECDHE-ECDSA-CHACHA20-POLY1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + "AES128-GCM-SHA256": tls.TLS_AES_128_GCM_SHA256, + "AES256-GCM-SHA384": tls.TLS_AES_256_GCM_SHA384, + "CHACHA20-POLY1305": tls.TLS_CHACHA20_POLY1305_SHA256, + "ECDHE-RSA-AES128-CBC-SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + "ECDHE-RSA-AES256-CBC-SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + "ECDHE-ECDSA-AES128-CBC-SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + "ECDHE-ECDSA-AES256-CBC-SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + "RSA-AES128-GCM-SHA256": tls.TLS_RSA_WITH_AES_128_GCM_SHA256, + "RSA-AES256-GCM-SHA384": tls.TLS_RSA_WITH_AES_256_GCM_SHA384, + "RSA-AES128-CBC-SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA, + "RSA-AES256-CBC-SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA, + "ECDHE-RSA-3DES-EDE-CBC-SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, + "RSA-3DES-EDE-CBC-SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA, + // Go standard names (TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + "TLS_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_RSA_WITH_AES_128_GCM_SHA256, + "TLS_RSA_WITH_AES_256_GCM_SHA384": tls.TLS_RSA_WITH_AES_256_GCM_SHA384, + "TLS_RSA_WITH_AES_128_CBC_SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA, + "TLS_RSA_WITH_AES_256_CBC_SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA, +} + +// insecureCipherIDs contains cipher suite IDs considered insecure. +var insecureCipherIDs = []uint16{ + tls.TLS_RSA_WITH_RC4_128_SHA, + tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA, + tls.TLS_RSA_WITH_AES_128_CBC_SHA256, + tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, + tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA, + tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, + tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, +} + +// IsInsecureCipher checks if a cipher suite is insecure. +// +// Parameters: +// - id: Cipher suite ID +// +// Returns: +// - bool: True if insecure +func IsInsecureCipher(id uint16) bool { + return slices.Contains(insecureCipherIDs, id) +} + +// ParseCipherSuites parses cipher suite name strings to TLS IDs. +// +// Parameters: +// - ciphers: Cipher suite name list +// +// Returns: +// - []uint16: Cipher suite ID list +// - error: Unknown or insecure cipher suite returns error +func ParseCipherSuites(ciphers []string) ([]uint16, error) { + result := make([]uint16, 0, len(ciphers)) + + for _, c := range ciphers { + id, ok := cipherNameToID[c] + if !ok { + return nil, fmt.Errorf("unknown cipher suite: %s", c) + } + // Check for insecure cipher suites + if IsInsecureCipher(id) { + return nil, fmt.Errorf("insecure cipher suite %s is not allowed", c) + } + result = append(result, id) + } + + return result, nil +} + +// ParseCipherSuitesLenient parses cipher suites without error on unknown names. +// Returns nil if no valid suites found, allowing fallback to defaults. +// +// Parameters: +// - ciphers: Cipher suite name list +// +// Returns: +// - []uint16: Cipher suite ID list (nil if none valid) +func ParseCipherSuitesLenient(ciphers []string) []uint16 { + var suites []uint16 + for _, c := range ciphers { + if id, ok := cipherNameToID[c]; ok && !IsInsecureCipher(id) { + suites = append(suites, id) + } + } + if len(suites) == 0 { + return nil // Use defaults + } + return suites +} + +// DefaultCipherSuites returns recommended cipher suites for TLS 1.2. +// +// Prioritizes forward secrecy and AEAD encryption algorithms. +// +// Returns: +// - []uint16: Cipher suite ID list +func DefaultCipherSuites() []uint16 { + return []uint16{ + tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + } +} + +// TLSVersionMap maps TLS version strings to tls constants. +// Supports TLSv1.0, TLSv1.1, TLSv1.2, TLSv1.3 formats (case insensitive). +// Empty string means use Go standard library default. +var TLSVersionMap = map[string]uint16{ + "TLSV1.0": tls.VersionTLS10, + "TLSV1.1": tls.VersionTLS11, + "TLSV1.2": tls.VersionTLS12, + "TLSV1.3": tls.VersionTLS13, + "": 0, // Empty string means use default +} \ No newline at end of file diff --git a/internal/stream/ssl.go b/internal/stream/ssl.go index 5b621a5..5e61bdd 100644 --- a/internal/stream/ssl.go +++ b/internal/stream/ssl.go @@ -131,12 +131,12 @@ func (m *SSLManager) GetTLSConfig() *tls.Config { // 设置协议版本 if len(m.config.Protocols) > 0 { - tlsConfig.MinVersion = parseMinTLSVersion(m.config.Protocols) + tlsConfig.MinVersion = sslutil.ParseMinTLSVersion(m.config.Protocols) } // 设置加密套件 if len(m.config.Ciphers) > 0 { - tlsConfig.CipherSuites = parseCipherSuites(m.config.Ciphers) + tlsConfig.CipherSuites = sslutil.ParseCipherSuitesLenient(m.config.Ciphers) } // 配置客户端证书验证(mTLS) @@ -183,7 +183,7 @@ func (m *ProxySSLManager) GetClientTLSConfig(serverName string) *tls.Config { // 设置协议版本 if len(m.config.Protocols) > 0 { - tlsConfig.MinVersion = parseMinTLSVersion(m.config.Protocols) + tlsConfig.MinVersion = sslutil.ParseMinTLSVersion(m.config.Protocols) } // 配置服务器证书验证 @@ -212,65 +212,3 @@ func (m *SSLManager) IsEnabled() bool { func (m *ProxySSLManager) IsEnabled() bool { return m.config.Enabled } - -// parseMinTLSVersion 解析最小 TLS 版本。 -// -// 参数: -// - protocols: 协议版本列表 -// -// 返回值: -// - uint16: TLS 版本常量 -func parseMinTLSVersion(protocols []string) uint16 { - for _, p := range protocols { - switch p { - case "TLSv1.3": - return tls.VersionTLS13 - case "TLSv1.2": - return tls.VersionTLS12 - } - } - return tls.VersionTLS12 -} - -// parseCipherSuites 解析加密套件列表。 -// -// 参数: -// - ciphers: 加密套件名称列表 -// -// 返回值: -// - []uint16: 加密套件 ID 列表 -func parseCipherSuites(ciphers []string) []uint16 { - var suites []uint16 - for _, c := range ciphers { - if id, ok := cipherNameToID[c]; ok { - suites = append(suites, id) - } - } - if len(suites) == 0 { - return nil // 使用默认值 - } - return suites -} - -// cipherNameToID 加密套件名称到 ID 的映射 -var cipherNameToID = map[string]uint16{ - "ECDHE-RSA-AES128-GCM-SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - "ECDHE-RSA-AES256-GCM-SHA384": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - "ECDHE-ECDSA-AES128-GCM-SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - "ECDHE-ECDSA-AES256-GCM-SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - "ECDHE-RSA-CHACHA20-POLY1305": tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - "ECDHE-ECDSA-CHACHA20-POLY1305": tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, - "AES128-GCM-SHA256": tls.TLS_AES_128_GCM_SHA256, - "AES256-GCM-SHA384": tls.TLS_AES_256_GCM_SHA384, - "CHACHA20-POLY1305": tls.TLS_CHACHA20_POLY1305_SHA256, - "ECDHE-RSA-AES128-CBC-SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, - "ECDHE-RSA-AES256-CBC-SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, - "ECDHE-ECDSA-AES128-CBC-SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, - "ECDHE-ECDSA-AES256-CBC-SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, - "RSA-AES128-GCM-SHA256": tls.TLS_RSA_WITH_AES_128_GCM_SHA256, - "RSA-AES256-GCM-SHA384": tls.TLS_RSA_WITH_AES_256_GCM_SHA384, - "RSA-AES128-CBC-SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA, - "RSA-AES256-CBC-SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA, - "ECDHE-RSA-3DES-EDE-CBC-SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, - "RSA-3DES-EDE-CBC-SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA, -} diff --git a/internal/stream/ssl_test.go b/internal/stream/ssl_test.go index bd57468..b9ac168 100644 --- a/internal/stream/ssl_test.go +++ b/internal/stream/ssl_test.go @@ -284,7 +284,7 @@ func TestParseMinTLSVersion(t *testing.T) { } for _, tt := range tests { - got := parseMinTLSVersion(tt.protocols) + got := sslutil.ParseMinTLSVersion(tt.protocols) if got != tt.wantVersion { t.Errorf("parseMinTLSVersion(%v) = %v, want %v", tt.protocols, got, tt.wantVersion) } @@ -316,7 +316,7 @@ func TestParseCipherSuites(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got := parseCipherSuites(tt.ciphers) + got := sslutil.ParseCipherSuitesLenient(tt.ciphers) if tt.wantLen == 0 && got != nil { t.Errorf("Expected nil, got %v", got) } else if tt.wantLen > 0 && len(got) != tt.wantLen {