Files
wehub-resource-sync 1b8708893a
Security Scan / tests (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:12:26 +08:00

103 lines
2.3 KiB
Go

package mitm
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"fmt"
"math/big"
"net"
"strings"
"time"
)
type leafEntry struct {
cert *tls.Certificate
expiresAt time.Time
}
const (
leafLifetime = 30 * 24 * time.Hour
minBeforeReissue = 24 * time.Hour
)
// IssueLeaf returns a TLS certificate for host, signed by this CA.
// Cached per host, re-minted when the cached cert is within
// minBeforeReissue of expiry.
func (c *CA) IssueLeaf(host string) (*tls.Certificate, error) {
if h, _, err := net.SplitHostPort(host); err == nil {
host = h
}
host = strings.ToLower(host)
now := time.Now()
c.mu.Lock()
if entry, ok := c.leaves[host]; ok {
if entry.expiresAt.After(now.Add(minBeforeReissue)) {
c.mu.Unlock()
return entry.cert, nil
}
delete(c.leaves, host)
}
c.mu.Unlock()
// Mint outside the lock so a slow ECDSA key-gen doesn't block
// concurrent lookups for already-cached hosts.
leaf, err := c.mintLeaf(host)
if err != nil {
return nil, err
}
c.mu.Lock()
c.leaves[host] = &leafEntry{
cert: leaf,
expiresAt: now.Add(leafLifetime),
}
c.mu.Unlock()
return leaf, nil
}
func (c *CA) mintLeaf(host string) (*tls.Certificate, error) {
leafKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return nil, fmt.Errorf("mitm: leaf key for %q: %w", host, err)
}
serial, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
if err != nil {
return nil, fmt.Errorf("mitm: leaf serial: %w", err)
}
now := time.Now().UTC()
tmpl := &x509.Certificate{
SerialNumber: serial,
Subject: pkix.Name{CommonName: host},
NotBefore: now.Add(-1 * time.Hour),
NotAfter: now.Add(leafLifetime),
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
ExtKeyUsage: []x509.ExtKeyUsage{
x509.ExtKeyUsageServerAuth,
},
BasicConstraintsValid: true,
}
if ip := net.ParseIP(host); ip != nil {
tmpl.IPAddresses = []net.IP{ip}
} else {
tmpl.DNSNames = []string{host}
}
der, err := x509.CreateCertificate(rand.Reader, tmpl, c.cert, &leafKey.PublicKey, c.key)
if err != nil {
return nil, fmt.Errorf("mitm: sign leaf for %q: %w", host, err)
}
return &tls.Certificate{
Certificate: [][]byte{der, c.cert.Raw},
PrivateKey: leafKey,
}, nil
}