update
All checks were successful
Build Multi-Platform Binaries / build-frontend (push) Successful in 30s
Build Multi-Platform Binaries / build-binaries (amd64, darwin, server, false) (push) Successful in 58s
Build Multi-Platform Binaries / build-binaries (amd64, linux, client, true) (push) Successful in 48s
Build Multi-Platform Binaries / build-binaries (amd64, linux, server, true) (push) Successful in 1m23s
Build Multi-Platform Binaries / build-binaries (amd64, windows, client, true) (push) Successful in 56s
Build Multi-Platform Binaries / build-binaries (amd64, windows, server, true) (push) Successful in 58s
Build Multi-Platform Binaries / build-binaries (arm, 7, linux, client, true) (push) Successful in 52s
Build Multi-Platform Binaries / build-binaries (arm, 7, linux, server, true) (push) Successful in 1m42s
Build Multi-Platform Binaries / build-binaries (arm64, darwin, server, false) (push) Successful in 1m19s
Build Multi-Platform Binaries / build-binaries (arm64, linux, client, true) (push) Successful in 54s
Build Multi-Platform Binaries / build-binaries (arm64, linux, server, true) (push) Successful in 2m3s
Build Multi-Platform Binaries / build-binaries (arm64, windows, server, false) (push) Successful in 1m1s

This commit is contained in:
Flik
2025-12-29 23:08:15 +08:00
parent d4984c8d78
commit 4d2a2a7117
10 changed files with 1429 additions and 35 deletions

View File

@@ -4,11 +4,17 @@ import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/hex"
"fmt"
"math/big"
"net"
"os"
"path/filepath"
"strings"
"time"
)
@@ -62,3 +68,90 @@ func ClientTLSConfig() *tls.Config {
MinVersion: tls.VersionTLS12,
}
}
// ClientTLSConfigWithTOFU 创建带 TOFU 验证的客户端 TLS 配置
// serverAddr: 服务器地址,用于存储指纹
// dataDir: 数据目录,用于存储指纹文件
// skipVerify: 是否跳过验证(测试环境使用)
func ClientTLSConfigWithTOFU(serverAddr, dataDir string, skipVerify bool) *tls.Config {
if skipVerify {
return &tls.Config{
InsecureSkipVerify: true,
MinVersion: tls.VersionTLS12,
}
}
return &tls.Config{
InsecureSkipVerify: true, // 必须为 true因为是自签名证书
MinVersion: tls.VersionTLS12,
VerifyPeerCertificate: func(rawCerts [][]byte, _ [][]*x509.Certificate) error {
return VerifyCertFingerprint(rawCerts, serverAddr, dataDir)
},
}
}
// CertFingerprint 计算证书指纹 (SHA256)
func CertFingerprint(certDER []byte) string {
hash := sha256.Sum256(certDER)
return hex.EncodeToString(hash[:])
}
// GetFingerprintPath 获取指纹文件路径
func GetFingerprintPath(serverAddr, dataDir string) string {
// 将服务器地址转换为安全的文件名
safeName := strings.ReplaceAll(serverAddr, ":", "_")
safeName = strings.ReplaceAll(safeName, "/", "_")
return filepath.Join(dataDir, ".fingerprint_"+safeName)
}
// LoadFingerprint 加载已保存的证书指纹
func LoadFingerprint(serverAddr, dataDir string) (string, error) {
path := GetFingerprintPath(serverAddr, dataDir)
data, err := os.ReadFile(path)
if err != nil {
return "", err
}
return strings.TrimSpace(string(data)), nil
}
// SaveFingerprint 保存证书指纹
func SaveFingerprint(serverAddr, dataDir, fingerprint string) error {
if err := os.MkdirAll(dataDir, 0700); err != nil {
return err
}
path := GetFingerprintPath(serverAddr, dataDir)
return os.WriteFile(path, []byte(fingerprint), 0600)
}
// VerifyCertFingerprint 验证证书指纹 (TOFU 模式)
func VerifyCertFingerprint(rawCerts [][]byte, serverAddr, dataDir string) error {
if len(rawCerts) == 0 {
return fmt.Errorf("no certificate provided")
}
// 计算当前证书指纹
currentFP := CertFingerprint(rawCerts[0])
// 尝试加载已保存的指纹
savedFP, err := LoadFingerprint(serverAddr, dataDir)
if err != nil {
// 首次连接,保存指纹
if os.IsNotExist(err) {
if saveErr := SaveFingerprint(serverAddr, dataDir, currentFP); saveErr != nil {
return fmt.Errorf("failed to save fingerprint: %w", saveErr)
}
return nil // 首次连接,信任此证书
}
return fmt.Errorf("failed to load fingerprint: %w", err)
}
// 验证指纹是否匹配
if savedFP != currentFP {
return fmt.Errorf("certificate fingerprint mismatch: possible MITM attack\n"+
" Expected: %s\n Got: %s\n"+
" If the server certificate was legitimately changed, delete: %s",
savedFP, currentFP, GetFingerprintPath(serverAddr, dataDir))
}
return nil
}