diff --git a/internal/data/cert.go b/internal/data/cert.go index f1833fd1..0ce34a3c 100644 --- a/internal/data/cert.go +++ b/internal/data/cert.go @@ -259,7 +259,7 @@ func (r *certRepo) ObtainSelfSigned(id uint) error { return err } - crt, key, err := pkgcert.GenerateSelfSigned(cert.Domains) + crt, key, err := pkgcert.GenerateSelfSignedRSA(cert.Domains) if err != nil { return err } diff --git a/internal/service/cli.go b/internal/service/cli.go index 3baf4f9f..abaa879c 100644 --- a/internal/service/cli.go +++ b/internal/service/cli.go @@ -392,7 +392,7 @@ func (s *CliService) HTTPSGenerate(ctx context.Context, cmd *cli.Command) error } if crt == nil || key == nil { - crt, key, err = cert.GenerateSelfSigned(names) + crt, key, err = cert.GenerateSelfSignedRSA(names) if err != nil { return err } diff --git a/pkg/cert/cert.go b/pkg/cert/cert.go index c9a680da..e506a3c5 100644 --- a/pkg/cert/cert.go +++ b/pkg/cert/cert.go @@ -199,3 +199,63 @@ func GenerateSelfSigned(names []string) (cert []byte, key []byte, err error) { 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) 解析 SAN:DNS + 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 +} diff --git a/pkg/cert/cert_test.go b/pkg/cert/cert_test.go index e3761daf..4b16c5b4 100644 --- a/pkg/cert/cert_test.go +++ b/pkg/cert/cert_test.go @@ -20,3 +20,10 @@ func (s *CertTestSuite) TestGenerateSelfSigned() { s.NotNil(pem) s.NotNil(key) } + +func (s *CertTestSuite) TestGenerateSelfSignedRSA() { + pem, key, err := GenerateSelfSignedRSA([]string{"haozi.dev"}) + s.Nil(err) + s.NotNil(pem) + s.NotNil(key) +}