553 lines
15 KiB
Go
553 lines
15 KiB
Go
package goproxy
|
||
|
||
import (
|
||
"crypto/ecdsa"
|
||
"crypto/elliptic"
|
||
"crypto/rand"
|
||
"crypto/rsa"
|
||
"crypto/tls"
|
||
"crypto/x509"
|
||
"crypto/x509/pkix"
|
||
"encoding/pem"
|
||
"fmt"
|
||
"hash/fnv"
|
||
"math/big"
|
||
"net"
|
||
"os"
|
||
"strings"
|
||
"time"
|
||
)
|
||
|
||
// 内置默认CA证书和私钥
|
||
var (
|
||
// 默认根证书
|
||
defaultRootCAPem = []byte(`-----BEGIN CERTIFICATE-----
|
||
MIICJzCCAcygAwIBAgIITWWCIQf8/VIwCgYIKoZIzj0EAwIwUzEOMAwGA1UEBhMF
|
||
Q2hpbmExDzANBgNVBAgTBkZ1SmlhbjEPMA0GA1UEBxMGWGlhbWVuMRAwDgYDVQQK
|
||
EwdHb3Byb3h5MQ0wCwYDVQQDEwRNYXJzMB4XDTIyMDMyNTA1NDgwMFoXDTQyMDQy
|
||
NTA1NDgwMFowUzEOMAwGA1UEBhMFQ2hpbmExDzANBgNVBAgTBkZ1SmlhbjEPMA0G
|
||
A1UEBxMGWGlhbWVuMRAwDgYDVQQKEwdHb3Byb3h5MQ0wCwYDVQQDEwRNYXJzMFkw
|
||
EwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEf0mhVJmuTmxnLimKshdEE4+PYdxvBfQX
|
||
mRgsFV5KHHmxOrVJBFC/nDetmGowkARShWtBsX1Irm4w6i6Qk2QliKOBiTCBhjAO
|
||
BgNVHQ8BAf8EBAMCAQYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMBIG
|
||
A1UdEwEB/wQIMAYBAf8CAQIwHQYDVR0OBBYEFBI5TkWYcvUIWsBAdffs833FnBrI
|
||
MCIGA1UdEQQbMBmBF3FpbmdxaWFubHVkYW9AZ21haWwuY29tMAoGCCqGSM49BAMC
|
||
A0kAMEYCIQCk1DhW7AmIW/n/QLftQq8BHZKLevWYJ813zdrNr5kXlwIhAIVvqglY
|
||
9BkYWg4NEe/mVO4C5Vtu4FnzNU9I+rFpXVSO
|
||
-----END CERTIFICATE-----
|
||
`)
|
||
// 默认根私钥
|
||
defaultRootKeyPem = []byte(`-----BEGIN EC PRIVATE KEY-----
|
||
MHcCAQEEIAXeEHO0FtFqQhTvsn/DT4g3rEos97+1Nibp9RfKOKhroAoGCCqGSM49
|
||
AwEHoUQDQgAEf0mhVJmuTmxnLimKshdEE4+PYdxvBfQXmRgsFV5KHHmxOrVJBFC/
|
||
nDetmGowkARShWtBsX1Irm4w6i6Qk2QliA==
|
||
-----END EC PRIVATE KEY-----
|
||
`)
|
||
)
|
||
|
||
// 加载和初始化默认根证书
|
||
var (
|
||
defaultRootCA *x509.Certificate
|
||
defaultRootKey *ecdsa.PrivateKey
|
||
)
|
||
|
||
func init() {
|
||
var err error
|
||
block, _ := pem.Decode(defaultRootCAPem)
|
||
if block == nil {
|
||
panic("解析默认根证书PEM块失败")
|
||
}
|
||
defaultRootCA, err = x509.ParseCertificate(block.Bytes)
|
||
if err != nil {
|
||
panic(fmt.Errorf("加载默认根证书失败: %s", err))
|
||
}
|
||
|
||
block, _ = pem.Decode(defaultRootKeyPem)
|
||
if block == nil {
|
||
panic("解析默认根私钥PEM块失败")
|
||
}
|
||
defaultRootKey, err = x509.ParseECPrivateKey(block.Bytes)
|
||
if err != nil {
|
||
panic(fmt.Errorf("加载默认根私钥失败: %s", err))
|
||
}
|
||
}
|
||
|
||
// CertManager 证书管理器
|
||
type CertManager struct {
|
||
// 证书缓存
|
||
cache CertificateCache
|
||
// 默认私钥,可用于多个证书共享
|
||
defaultPrivateKey interface{} // 改为interface{}以支持不同类型的私钥
|
||
// 默认使用ECDSA P-256曲线
|
||
curve elliptic.Curve
|
||
// 证书有效期(年)
|
||
validityYears int
|
||
// 是否使用ECDSA(否则使用RSA)
|
||
useECDSA bool
|
||
}
|
||
|
||
// NewCertManager 创建证书管理器
|
||
func NewCertManager(cache CertificateCache, options ...CertManagerOption) *CertManager {
|
||
manager := &CertManager{
|
||
cache: cache,
|
||
curve: elliptic.P256(), // 默认使用P-256曲线
|
||
validityYears: 1, // 默认证书有效期1年
|
||
useECDSA: true, // 默认使用ECDSA
|
||
}
|
||
|
||
// 应用选项
|
||
for _, option := range options {
|
||
option(manager)
|
||
}
|
||
|
||
return manager
|
||
}
|
||
|
||
// CertManagerOption 证书管理器选项
|
||
type CertManagerOption func(*CertManager)
|
||
|
||
// WithUseECDSA 设置是否使用ECDSA(否则使用RSA)
|
||
func WithUseECDSA(useECDSA bool) CertManagerOption {
|
||
return func(m *CertManager) {
|
||
m.useECDSA = useECDSA
|
||
}
|
||
}
|
||
|
||
// WithDefaultPrivateKey 设置是否使用默认私钥
|
||
func WithDefaultPrivateKey(enable bool) CertManagerOption {
|
||
return func(m *CertManager) {
|
||
if enable {
|
||
if m.useECDSA {
|
||
// 生成ECDSA私钥
|
||
priv, err := ecdsa.GenerateKey(m.curve, rand.Reader)
|
||
if err != nil {
|
||
panic(fmt.Errorf("生成默认ECDSA私钥失败: %s", err))
|
||
}
|
||
m.defaultPrivateKey = priv
|
||
} else {
|
||
// 生成RSA私钥
|
||
priv, err := rsa.GenerateKey(rand.Reader, 2048)
|
||
if err != nil {
|
||
panic(fmt.Errorf("生成默认RSA私钥失败: %s", err))
|
||
}
|
||
m.defaultPrivateKey = priv
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// WithCurve 设置椭圆曲线
|
||
func WithCurve(curve elliptic.Curve) CertManagerOption {
|
||
return func(m *CertManager) {
|
||
m.curve = curve
|
||
}
|
||
}
|
||
|
||
// WithValidityYears 设置证书有效期(年)
|
||
func WithValidityYears(years int) CertManagerOption {
|
||
return func(m *CertManager) {
|
||
if years > 0 {
|
||
m.validityYears = years
|
||
}
|
||
}
|
||
}
|
||
|
||
// GenerateTLSConfig 为指定主机生成TLS配置
|
||
func (m *CertManager) GenerateTLSConfig(host string) (*tls.Config, error) {
|
||
// 处理可能的端口
|
||
if h, _, err := net.SplitHostPort(host); err == nil {
|
||
host = h
|
||
}
|
||
|
||
// 检查证书缓存
|
||
if m.cache != nil {
|
||
// 检查主域名和子域名
|
||
fields := strings.Split(host, ".")
|
||
domains := []string{host}
|
||
|
||
// 添加父域名
|
||
if len(fields) > 2 {
|
||
domains = append(domains, strings.Join(fields[1:], "."))
|
||
}
|
||
|
||
// 查找缓存
|
||
for _, domain := range domains {
|
||
if cert := m.cache.Get(domain); cert != nil {
|
||
return &tls.Config{
|
||
Certificates: []tls.Certificate{*cert},
|
||
}, nil
|
||
}
|
||
}
|
||
}
|
||
|
||
// 生成新证书
|
||
cert, err := m.GenerateCertificate(host, defaultRootCA, defaultRootKey)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// 缓存证书
|
||
if m.cache != nil {
|
||
// 缓存主机名
|
||
m.cache.Set(host, cert)
|
||
|
||
// 如果是IP地址,不进行其他处理
|
||
if net.ParseIP(host) != nil {
|
||
return &tls.Config{
|
||
Certificates: []tls.Certificate{*cert},
|
||
}, nil
|
||
}
|
||
|
||
// 缓存域名和子域名证书
|
||
fields := strings.Split(host, ".")
|
||
if len(fields) >= 2 {
|
||
// 缓存主域名
|
||
domain := strings.Join(fields[1:], ".")
|
||
m.cache.Set(domain, cert)
|
||
}
|
||
}
|
||
|
||
return &tls.Config{
|
||
Certificates: []tls.Certificate{*cert},
|
||
}, nil
|
||
}
|
||
|
||
// GenerateCertificate 生成证书
|
||
func (m *CertManager) GenerateCertificate(host string, rootCA *x509.Certificate, rootKey *ecdsa.PrivateKey) (*tls.Certificate, error) {
|
||
// 准备私钥
|
||
var priv interface{}
|
||
var pubKey interface{}
|
||
var err error
|
||
|
||
// 使用默认私钥或生成新私钥
|
||
if m.defaultPrivateKey != nil {
|
||
priv = m.defaultPrivateKey
|
||
} else if m.useECDSA {
|
||
// 生成ECDSA私钥
|
||
ecdsaKey, err := ecdsa.GenerateKey(m.curve, rand.Reader)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("生成ECDSA私钥失败: %s", err)
|
||
}
|
||
priv = ecdsaKey
|
||
pubKey = &ecdsaKey.PublicKey
|
||
} else {
|
||
// 生成RSA私钥
|
||
rsaKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("生成RSA私钥失败: %s", err)
|
||
}
|
||
priv = rsaKey
|
||
pubKey = &rsaKey.PublicKey
|
||
}
|
||
|
||
// 获取公钥
|
||
if pubKey == nil {
|
||
switch k := priv.(type) {
|
||
case *ecdsa.PrivateKey:
|
||
pubKey = &k.PublicKey
|
||
case *rsa.PrivateKey:
|
||
pubKey = &k.PublicKey
|
||
default:
|
||
return nil, fmt.Errorf("不支持的私钥类型")
|
||
}
|
||
}
|
||
|
||
// 创建证书模板
|
||
template := m.createCertificateTemplate(host)
|
||
|
||
// 签名证书
|
||
derBytes, err := x509.CreateCertificate(rand.Reader, template, rootCA, pubKey, rootKey)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("创建证书失败: %s", err)
|
||
}
|
||
|
||
// 编码为PEM格式
|
||
certPEM := pem.EncodeToMemory(&pem.Block{
|
||
Type: "CERTIFICATE",
|
||
Bytes: derBytes,
|
||
})
|
||
|
||
// 编码私钥
|
||
var keyPEM []byte
|
||
switch k := priv.(type) {
|
||
case *ecdsa.PrivateKey:
|
||
privBytes, err := x509.MarshalECPrivateKey(k)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("序列化ECDSA私钥失败: %s", err)
|
||
}
|
||
keyPEM = pem.EncodeToMemory(&pem.Block{
|
||
Type: "EC PRIVATE KEY",
|
||
Bytes: privBytes,
|
||
})
|
||
case *rsa.PrivateKey:
|
||
privBytes := x509.MarshalPKCS1PrivateKey(k)
|
||
keyPEM = pem.EncodeToMemory(&pem.Block{
|
||
Type: "RSA PRIVATE KEY",
|
||
Bytes: privBytes,
|
||
})
|
||
default:
|
||
return nil, fmt.Errorf("不支持的私钥类型")
|
||
}
|
||
|
||
// 创建TLS证书
|
||
tlsCert, err := tls.X509KeyPair(certPEM, keyPEM)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("创建TLS证书对失败: %s", err)
|
||
}
|
||
|
||
return &tlsCert, nil
|
||
}
|
||
|
||
// createCertificateTemplate 创建证书模板
|
||
func (m *CertManager) createCertificateTemplate(host string) *x509.Certificate {
|
||
// 使用基于主机名的哈希值作为序列号
|
||
fv := fnv.New64a()
|
||
fv.Write([]byte(host))
|
||
serialNumber := big.NewInt(0).SetUint64(fv.Sum64())
|
||
|
||
// 准备模板
|
||
template := &x509.Certificate{
|
||
SerialNumber: serialNumber,
|
||
Subject: pkix.Name{
|
||
CommonName: host,
|
||
Organization: []string{"GoProxy Dynamic CA"},
|
||
Country: []string{"CN"},
|
||
Province: []string{"GuangDong"},
|
||
Locality: []string{"Guangzhou"},
|
||
},
|
||
NotBefore: time.Now().Add(-10 * time.Minute), // 提前10分钟生效,容忍时间偏差
|
||
NotAfter: time.Now().AddDate(m.validityYears, 0, 0),
|
||
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
|
||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
|
||
BasicConstraintsValid: true,
|
||
IsCA: false,
|
||
}
|
||
|
||
// 处理IP地址和域名
|
||
ipAddr := net.ParseIP(host)
|
||
if ipAddr != nil {
|
||
template.IPAddresses = []net.IP{ipAddr}
|
||
} else {
|
||
// 移除可能的端口部分
|
||
if strings.Contains(host, ":") {
|
||
host = strings.Split(host, ":")[0]
|
||
}
|
||
|
||
// 将主机名添加到DNS名称列表
|
||
template.DNSNames = []string{host}
|
||
|
||
// 添加通配符域名支持
|
||
fields := strings.Split(host, ".")
|
||
fieldNum := len(fields)
|
||
|
||
// 为每一级子域名添加通配符
|
||
for i := 0; i <= (fieldNum - 2); i++ {
|
||
wildcardDomain := "*." + strings.Join(fields[i:], ".")
|
||
// 避免重复
|
||
if wildcardDomain != host {
|
||
template.DNSNames = append(template.DNSNames, wildcardDomain)
|
||
}
|
||
}
|
||
}
|
||
|
||
return template
|
||
}
|
||
|
||
// LoadCAFromFiles 从文件加载CA证书和私钥
|
||
func LoadCAFromFiles(certFile, keyFile string) (*x509.Certificate, *ecdsa.PrivateKey, error) {
|
||
// 读取CA证书
|
||
caCertPEM, err := os.ReadFile(certFile)
|
||
if err != nil {
|
||
return nil, nil, fmt.Errorf("读取CA证书文件失败: %s", err)
|
||
}
|
||
|
||
block, _ := pem.Decode(caCertPEM)
|
||
if block == nil {
|
||
return nil, nil, fmt.Errorf("解析CA证书PEM块失败")
|
||
}
|
||
|
||
caCert, err := x509.ParseCertificate(block.Bytes)
|
||
if err != nil {
|
||
return nil, nil, fmt.Errorf("解析CA证书失败: %s", err)
|
||
}
|
||
|
||
// 读取CA私钥
|
||
caKeyPEM, err := os.ReadFile(keyFile)
|
||
if err != nil {
|
||
return nil, nil, fmt.Errorf("读取CA私钥文件失败: %s", err)
|
||
}
|
||
|
||
block, _ = pem.Decode(caKeyPEM)
|
||
if block == nil {
|
||
return nil, nil, fmt.Errorf("解析CA私钥PEM块失败")
|
||
}
|
||
|
||
var caKey *ecdsa.PrivateKey
|
||
|
||
// 尝试不同的私钥格式
|
||
switch block.Type {
|
||
case "EC PRIVATE KEY":
|
||
caKey, err = x509.ParseECPrivateKey(block.Bytes)
|
||
if err != nil {
|
||
return nil, nil, fmt.Errorf("解析EC私钥失败: %s", err)
|
||
}
|
||
case "PRIVATE KEY":
|
||
key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
|
||
if err != nil {
|
||
return nil, nil, fmt.Errorf("解析PKCS8私钥失败: %s", err)
|
||
}
|
||
var ok bool
|
||
caKey, ok = key.(*ecdsa.PrivateKey)
|
||
if !ok {
|
||
return nil, nil, fmt.Errorf("私钥不是ECDSA类型")
|
||
}
|
||
default:
|
||
return nil, nil, fmt.Errorf("不支持的私钥类型: %s", block.Type)
|
||
}
|
||
|
||
return caCert, caKey, nil
|
||
}
|
||
|
||
// GetDefaultRootCA 获取默认根证书和私钥
|
||
func GetDefaultRootCA() (*x509.Certificate, *ecdsa.PrivateKey) {
|
||
return defaultRootCA, defaultRootKey
|
||
}
|
||
|
||
// GenerateRootCA 生成新的根证书和私钥
|
||
func GenerateRootCA(validYears int) (*x509.Certificate, *ecdsa.PrivateKey, error) {
|
||
// 生成私钥
|
||
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||
if err != nil {
|
||
return nil, nil, fmt.Errorf("生成根证书私钥失败: %s", err)
|
||
}
|
||
|
||
// 随机生成序列号
|
||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||
if err != nil {
|
||
return nil, nil, fmt.Errorf("生成序列号失败: %s", err)
|
||
}
|
||
|
||
// 创建根证书模板
|
||
notBefore := time.Now().Add(-10 * time.Minute)
|
||
notAfter := notBefore.AddDate(validYears, 0, 0)
|
||
|
||
template := &x509.Certificate{
|
||
SerialNumber: serialNumber,
|
||
Subject: pkix.Name{
|
||
CommonName: "GoProxy Root CA",
|
||
Organization: []string{"GoProxy"},
|
||
Country: []string{"CN"},
|
||
Province: []string{"GuangDong"},
|
||
Locality: []string{"Guangzhou"},
|
||
},
|
||
NotBefore: notBefore,
|
||
NotAfter: notAfter,
|
||
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
|
||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
|
||
BasicConstraintsValid: true,
|
||
IsCA: true,
|
||
MaxPathLen: 2,
|
||
}
|
||
|
||
// 自签名
|
||
derBytes, err := x509.CreateCertificate(rand.Reader, template, template, &priv.PublicKey, priv)
|
||
if err != nil {
|
||
return nil, nil, fmt.Errorf("创建根证书失败: %s", err)
|
||
}
|
||
|
||
// 解析生成的证书
|
||
cert, err := x509.ParseCertificate(derBytes)
|
||
if err != nil {
|
||
return nil, nil, fmt.Errorf("解析生成的根证书失败: %s", err)
|
||
}
|
||
|
||
return cert, priv, nil
|
||
}
|
||
|
||
// SaveCertificateToFile 将证书和私钥保存到文件
|
||
func SaveCertificateToFile(cert *x509.Certificate, key *ecdsa.PrivateKey, certFile, keyFile string) error {
|
||
// 保存证书
|
||
certPEM := pem.EncodeToMemory(&pem.Block{
|
||
Type: "CERTIFICATE",
|
||
Bytes: cert.Raw,
|
||
})
|
||
if err := os.WriteFile(certFile, certPEM, 0o644); err != nil {
|
||
return fmt.Errorf("保存证书到文件失败: %s", err)
|
||
}
|
||
|
||
// 保存私钥
|
||
keyBytes, err := x509.MarshalECPrivateKey(key)
|
||
if err != nil {
|
||
return fmt.Errorf("序列化私钥失败: %s", err)
|
||
}
|
||
keyPEM := pem.EncodeToMemory(&pem.Block{
|
||
Type: "EC PRIVATE KEY",
|
||
Bytes: keyBytes,
|
||
})
|
||
if err := os.WriteFile(keyFile, keyPEM, 0o600); err != nil {
|
||
return fmt.Errorf("保存私钥到文件失败: %s", err)
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// GenerateRootCA 生成根证书
|
||
func (m *CertManager) GenerateRootCA() (*x509.Certificate, interface{}, error) {
|
||
// 生成私钥
|
||
var priv interface{}
|
||
var pubKey interface{}
|
||
var err error
|
||
|
||
if m.useECDSA {
|
||
// 生成ECDSA私钥
|
||
ecdsaKey, err := ecdsa.GenerateKey(m.curve, rand.Reader)
|
||
if err != nil {
|
||
return nil, nil, fmt.Errorf("生成ECDSA根私钥失败: %s", err)
|
||
}
|
||
priv = ecdsaKey
|
||
pubKey = &ecdsaKey.PublicKey
|
||
} else {
|
||
// 生成RSA私钥
|
||
rsaKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||
if err != nil {
|
||
return nil, nil, fmt.Errorf("生成RSA根私钥失败: %s", err)
|
||
}
|
||
priv = rsaKey
|
||
pubKey = &rsaKey.PublicKey
|
||
}
|
||
|
||
// 创建根证书模板
|
||
template := &x509.Certificate{
|
||
SerialNumber: big.NewInt(1),
|
||
Subject: pkix.Name{
|
||
CommonName: "GoProxy Root CA",
|
||
Organization: []string{"GoProxy Authority"},
|
||
Country: []string{"CN"},
|
||
Province: []string{"GuangDong"},
|
||
Locality: []string{"Guangzhou"},
|
||
},
|
||
NotBefore: time.Now().Add(-10 * time.Minute),
|
||
NotAfter: time.Now().AddDate(10, 0, 0), // 10年有效期
|
||
KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
|
||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
|
||
BasicConstraintsValid: true,
|
||
IsCA: true,
|
||
MaxPathLen: 1,
|
||
}
|
||
|
||
// 自签名
|
||
derBytes, err := x509.CreateCertificate(rand.Reader, template, template, pubKey, priv)
|
||
if err != nil {
|
||
return nil, nil, fmt.Errorf("创建根证书失败: %s", err)
|
||
}
|
||
|
||
// 解析证书
|
||
cert, err := x509.ParseCertificate(derBytes)
|
||
if err != nil {
|
||
return nil, nil, fmt.Errorf("解析生成的根证书失败: %s", err)
|
||
}
|
||
|
||
return cert, priv, nil
|
||
}
|