2
0
mirror of https://github.com/acepanel/panel.git synced 2026-02-04 03:07:20 +08:00
Files
panel/pkg/cert/cert.go
2026-01-31 18:40:41 +08:00

215 lines
5.5 KiB
Go
Raw 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 cert
import (
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"errors"
"fmt"
"math/big"
"net"
"strings"
"time"
)
func ParseCert(crt []byte) (x509.Certificate, error) {
certBlock, _ := pem.Decode(crt)
if certBlock == nil {
return x509.Certificate{}, errors.New("invalid PEM block")
}
cert, err := x509.ParseCertificate(certBlock.Bytes)
if err != nil {
return x509.Certificate{}, err
}
return *cert, nil
}
func ParseKey(key []byte) (crypto.Signer, error) {
keyBlockDER, _ := pem.Decode(key)
if keyBlockDER == nil {
return nil, errors.New("invalid PEM block")
}
if keyBlockDER.Type != "PRIVATE KEY" && !strings.HasSuffix(keyBlockDER.Type, " PRIVATE KEY") {
return nil, fmt.Errorf("unknown PEM header %q", keyBlockDER.Type)
}
if parse, err := x509.ParsePKCS1PrivateKey(keyBlockDER.Bytes); err == nil {
return parse, nil
}
if parse, err := x509.ParsePKCS8PrivateKey(keyBlockDER.Bytes); err == nil {
switch parse.(type) {
case *rsa.PrivateKey, *ecdsa.PrivateKey, ed25519.PrivateKey:
return parse.(crypto.Signer), nil
default:
return nil, fmt.Errorf("found unknown private key type in PKCS#8 wrapping: %T", key)
}
}
return x509.ParseECPrivateKey(keyBlockDER.Bytes)
}
func EncodeCert(cert x509.Certificate) ([]byte, error) {
pemCert := pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}
return pem.EncodeToMemory(&pemCert), nil
}
func EncodeKey(key crypto.Signer) ([]byte, error) {
var pemType string
var keyBytes []byte
switch key := key.(type) {
case *ecdsa.PrivateKey:
var err error
pemType = "EC"
keyBytes, err = x509.MarshalECPrivateKey(key)
if err != nil {
return nil, err
}
case *rsa.PrivateKey:
pemType = "RSA"
keyBytes = x509.MarshalPKCS1PrivateKey(key)
case ed25519.PrivateKey:
var err error
pemType = "ED25519"
keyBytes, err = x509.MarshalPKCS8PrivateKey(key)
if err != nil {
return nil, err
}
default:
return nil, fmt.Errorf("unsupported key type %T", key)
}
pemKey := pem.Block{Type: pemType + " PRIVATE KEY", Bytes: keyBytes}
return pem.EncodeToMemory(&pemKey), nil
}
// GenerateSelfSigned 生成自签名证书
func GenerateSelfSigned(names []string) (cert []byte, key []byte, err error) {
// 1) 生成 ECDSA P-256 密钥对
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return nil, nil, fmt.Errorf("generate ecdsa key: %w", err)
}
// 2) 解析 SAN
var dnsNames []string
var ipAddrs []net.IP
for _, n := range names {
if ip := net.ParseIP(n); ip != nil {
ipAddrs = append(ipAddrs, ip)
} else if n != "" {
dnsNames = append(dnsNames, n)
}
}
if len(dnsNames) == 0 && len(ipAddrs) == 0 {
return nil, nil, fmt.Errorf("names is empty: SAN must not be empty")
}
// 3) 随机 128 位序列号
serialLimit := new(big.Int).Lsh(big.NewInt(1), 128)
serial, err := rand.Int(rand.Reader, serialLimit)
if err != nil {
return nil, nil, err
}
now := time.Now().Add(-5 * time.Minute) // 避免时钟偏差导致 not yet valid
// 4) 组装证书模板
tmpl := x509.Certificate{
SerialNumber: serial,
Subject: pkix.Name{CommonName: "AcePanel"},
NotBefore: now,
NotAfter: now.AddDate(1, 0, 0), // 1 年
DNSNames: dnsNames,
IPAddresses: ipAddrs,
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
}
der, err := x509.CreateCertificate(rand.Reader, &tmpl, &tmpl, &priv.PublicKey, priv)
if err != nil {
return nil, nil, err
}
// 5) 输出 PEM 证书
cert = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: der})
keyBytes, err := x509.MarshalPKCS8PrivateKey(priv)
if err != nil {
return nil, nil, err
}
key = pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: keyBytes})
return cert, key, nil
}
// GenerateSelfSignedRSA 生成 RSA-3072 自签名证书
func GenerateSelfSignedRSA(hosts []string) (certPEM []byte, keyPEM []byte, err error) {
// 1) 生成 RSA 3072 私钥
priv, err := rsa.GenerateKey(rand.Reader, 3072)
if err != nil {
return nil, nil, err
}
// 2) 解析 SANDNS + IP
var dnsNames []string
var ipAddrs []net.IP
for _, h := range hosts {
if ip := net.ParseIP(h); ip != nil {
ipAddrs = append(ipAddrs, ip)
} else if h != "" {
dnsNames = append(dnsNames, h)
}
}
if len(dnsNames) == 0 && len(ipAddrs) == 0 {
return nil, nil, fmt.Errorf("hosts is empty: SAN must not be empty")
}
// 3) 随机 128 位序列号
serialLimit := new(big.Int).Lsh(big.NewInt(1), 128)
serial, err := rand.Int(rand.Reader, serialLimit)
if err != nil {
return nil, nil, err
}
now := time.Now().Add(-5 * time.Minute) // 避免时钟偏差导致 not yet valid
// 4) 组装证书模板
tmpl := x509.Certificate{
SerialNumber: serial,
Subject: pkix.Name{CommonName: "AcePanel"},
NotBefore: now,
NotAfter: now.AddDate(1, 0, 0), // 1 年
DNSNames: dnsNames,
IPAddresses: ipAddrs,
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
}
der, err := x509.CreateCertificate(rand.Reader, &tmpl, &tmpl, &priv.PublicKey, priv)
if err != nil {
return nil, nil, err
}
// 5) 输出 PEM
certPEM = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: der})
keyBytes := x509.MarshalPKCS1PrivateKey(priv)
keyPEM = pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: keyBytes})
return certPEM, keyPEM, nil
}