From 4e8f0cf5e8bf2bbf23de28f67d1fa1f6887dc571 Mon Sep 17 00:00:00 2001 From: v-me-50 Date: Thu, 26 Jun 2025 16:51:26 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=8B=E8=BD=BD=E8=AF=81=E4=B9=A6=E5=85=BC?= =?UTF-8?q?=E5=AE=B9jks?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/api/cert.go | 22 +++++++++++++++++ backend/public/cert.go | 54 +++++++++++++++++++++++++++++++++++++++++ go.mod | 2 ++ go.sum | 4 +++ 4 files changed, 82 insertions(+) diff --git a/backend/app/api/cert.go b/backend/app/api/cert.go index e54f19c..fd964f5 100644 --- a/backend/app/api/cert.go +++ b/backend/app/api/cert.go @@ -144,6 +144,28 @@ func DownloadCert(c *gin.Context) { return } } + // cert.jks + jksData, err := public.PfxToJks(pfxData, pfxPassword, pfxPassword, "allinssl") + if err == nil && jksData != nil { + jksWriter, err := zipWriter.Create("Tomcat/cert.jks") + if err != nil { + public.FailMsg(c, err.Error()) + return + } + if _, err := jksWriter.Write(jksData.Bytes()); err != nil { + public.FailMsg(c, err.Error()) + return + } + txtWriter, err := zipWriter.Create("Tomcat/passwd.txt") + if err != nil { + public.FailMsg(c, err.Error()) + return + } + if _, err := txtWriter.Write([]byte(pfxPassword)); err != nil { + public.FailMsg(c, err.Error()) + return + } + } // 关闭 zipWriter if err := zipWriter.Close(); err != nil { diff --git a/backend/public/cert.go b/backend/public/cert.go index b813185..06cecb3 100644 --- a/backend/public/cert.go +++ b/backend/public/cert.go @@ -1,6 +1,7 @@ package public import ( + "bytes" "crypto" "crypto/ecdsa" "crypto/ed25519" @@ -12,6 +13,7 @@ import ( "encoding/pem" "errors" "fmt" + "github.com/pavlo-v-chernykh/keystore-go/v4" "software.sslmate.com/src/go-pkcs12" "strings" "time" @@ -184,3 +186,55 @@ func PEMToPFX(certPEM, keyPEM, pfxPassword string) ([]byte, error) { return pfxData, nil } + +// PfxToJks 将PFX格式证书转换为JKS格式 +func PfxToJks(pfxData []byte, pfxPassword, jksPassword, alias string) (*bytes.Buffer, error) { + if pfxPassword == "" { + return nil, fmt.Errorf("PFX 密码不能为空") + } + if jksPassword == "" { + jksPassword = pfxPassword + } + if alias == "" { + alias = "mycert" + } + // 解析 PFX,提取私钥、证书链 + priv, cert, caCerts, err := pkcs12.DecodeChain(pfxData, pfxPassword) + if err != nil { + return nil, fmt.Errorf("解析 PFX 失败: %w", err) + } + + // 序列化私钥,兼容多种类型 + pkBytes, err := x509.MarshalPKCS8PrivateKey(priv) + if err != nil { + return nil, fmt.Errorf("私钥序列化失败: %w", err) + } + + // 构建证书链 + certChain := make([]keystore.Certificate, 0, len(caCerts)+1) + certChain = append(certChain, keystore.Certificate{ + Type: "X.509", + Content: cert.Raw, + }) + for _, c := range caCerts { + certChain = append(certChain, keystore.Certificate{ + Type: "X.509", + Content: c.Raw, + }) + } + + // 创建 JKS 并写入条目 + ks := keystore.New() + ks.SetPrivateKeyEntry(alias, keystore.PrivateKeyEntry{ + PrivateKey: pkBytes, + CertificateChain: certChain, + }, []byte(jksPassword)) + + // 写入到 Buffer + var buf bytes.Buffer + if err := ks.Store(&buf, []byte(jksPassword)); err != nil { + return nil, fmt.Errorf("生成 JKS 失败: %w", err) + } + + return &buf, nil +} diff --git a/go.mod b/go.mod index 126370a..30b64a0 100644 --- a/go.mod +++ b/go.mod @@ -27,12 +27,14 @@ require ( github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible github.com/mitchellh/go-ps v1.0.0 github.com/mojocn/base64Captcha v1.3.8 + github.com/pavlo-v-chernykh/keystore-go/v4 v4.5.0 github.com/qiniu/go-sdk/v7 v7.25.3 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1128 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl v1.0.1124 github.com/volcengine/volcengine-go-sdk v1.1.11 golang.org/x/crypto v0.37.0 modernc.org/sqlite v1.37.0 + software.sslmate.com/src/go-pkcs12 v0.5.0 ) require ( diff --git a/go.sum b/go.sum index 10ee474..82ac184 100644 --- a/go.sum +++ b/go.sum @@ -595,6 +595,8 @@ github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b/go.mo github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pavlo-v-chernykh/keystore-go/v4 v4.5.0 h1:2nosf3P75OZv2/ZO/9Px5ZgZ5gbKrzA3joN1QMfOGMQ= +github.com/pavlo-v-chernykh/keystore-go/v4 v4.5.0/go.mod h1:lAVhWwbNaveeJmxrxuSTxMgKpF6DjnuVpn6T8WiBwYQ= github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= github.com/performancecopilot/speed/v4 v4.0.0/go.mod h1:qxrSyuDGrTOWfV+uKRFhfxw6h/4HXRGUiZiufxo49BM= @@ -1243,3 +1245,5 @@ rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= +software.sslmate.com/src/go-pkcs12 v0.5.0 h1:EC6R394xgENTpZ4RltKydeDUjtlM5drOYIG9c6TVj2M= +software.sslmate.com/src/go-pkcs12 v0.5.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI=