Files
demo/certificate.go
2025-03-14 18:50:49 +00:00

553 lines
15 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}