diff --git a/internal/deployer/deployer.go b/internal/deployer/deployer.go index fbb582f5..b05147f0 100644 --- a/internal/deployer/deployer.go +++ b/internal/deployer/deployer.go @@ -1,21 +1,15 @@ package deployer import ( - "bytes" "context" "encoding/json" - "encoding/pem" "errors" "fmt" - "time" - "github.com/pavlo-v-chernykh/keystore-go/v4" "github.com/pocketbase/pocketbase/models" - "software.sslmate.com/src/go-pkcs12" "github.com/usual2970/certimate/internal/applicant" "github.com/usual2970/certimate/internal/domain" - "github.com/usual2970/certimate/internal/pkg/utils/x509" "github.com/usual2970/certimate/internal/utils/app" ) @@ -167,57 +161,3 @@ func toStr(tag string, data any) string { byts, _ := json.Marshal(data) return tag + ":" + string(byts) } - -func convertPEMToPFX(certificate string, privateKey string, password string) ([]byte, error) { - cert, err := x509.ParseCertificateFromPEM(certificate) - if err != nil { - return nil, err - } - - privkey, err := x509.ParsePKCS1PrivateKeyFromPEM(privateKey) - if err != nil { - return nil, err - } - - pfxData, err := pkcs12.LegacyRC2.Encode(privkey, cert, nil, password) - if err != nil { - return nil, err - } - - return pfxData, nil -} - -func convertPEMToJKS(certificate string, privateKey string, alias string, keypass string, storepass string) ([]byte, error) { - certBlock, _ := pem.Decode([]byte(certificate)) - if certBlock == nil { - return nil, errors.New("failed to decode certificate PEM") - } - - privkeyBlock, _ := pem.Decode([]byte(privateKey)) - if privkeyBlock == nil { - return nil, errors.New("failed to decode private key PEM") - } - - ks := keystore.New() - entry := keystore.PrivateKeyEntry{ - CreationTime: time.Now(), - PrivateKey: privkeyBlock.Bytes, - CertificateChain: []keystore.Certificate{ - { - Type: "X509", - Content: certBlock.Bytes, - }, - }, - } - - if err := ks.SetPrivateKeyEntry(alias, entry, []byte(keypass)); err != nil { - return nil, err - } - - var buf bytes.Buffer - if err := ks.Store(&buf, []byte(storepass)); err != nil { - return nil, err - } - - return buf.Bytes(), nil -} diff --git a/internal/deployer/local.go b/internal/deployer/local.go index 7562e2f6..3c693d30 100644 --- a/internal/deployer/local.go +++ b/internal/deployer/local.go @@ -11,6 +11,7 @@ import ( xerrors "github.com/pkg/errors" "github.com/usual2970/certimate/internal/pkg/utils/fs" + "github.com/usual2970/certimate/internal/pkg/utils/x509" ) type LocalDeployer struct { @@ -73,7 +74,7 @@ func (d *LocalDeployer) Deploy(ctx context.Context) error { d.infos = append(d.infos, toStr("保存私钥成功", nil)) case certFormatPFX: - pfxData, err := convertPEMToPFX( + pfxData, err := x509.TransformCertificateFromPEMToPFX( d.option.Certificate.Certificate, d.option.Certificate.PrivateKey, d.option.DeployConfig.GetConfigAsString("pfxPassword"), @@ -89,7 +90,7 @@ func (d *LocalDeployer) Deploy(ctx context.Context) error { d.infos = append(d.infos, toStr("保存证书成功", nil)) case certFormatJKS: - jksData, err := convertPEMToJKS( + jksData, err := x509.TransformCertificateFromPEMToJKS( d.option.Certificate.Certificate, d.option.Certificate.PrivateKey, d.option.DeployConfig.GetConfigAsString("jksAlias"), diff --git a/internal/deployer/ssh.go b/internal/deployer/ssh.go index 8a5bfa1d..96f8bdd2 100644 --- a/internal/deployer/ssh.go +++ b/internal/deployer/ssh.go @@ -14,6 +14,7 @@ import ( "golang.org/x/crypto/ssh" "github.com/usual2970/certimate/internal/domain" + "github.com/usual2970/certimate/internal/pkg/utils/x509" ) type SSHDeployer struct { @@ -78,7 +79,7 @@ func (d *SSHDeployer) Deploy(ctx context.Context) error { d.infos = append(d.infos, toStr("SSH 上传私钥成功", nil)) case certFormatPFX: - pfxData, err := convertPEMToPFX( + pfxData, err := x509.TransformCertificateFromPEMToPFX( d.option.Certificate.Certificate, d.option.Certificate.PrivateKey, d.option.DeployConfig.GetConfigAsString("pfxPassword"), @@ -94,7 +95,7 @@ func (d *SSHDeployer) Deploy(ctx context.Context) error { d.infos = append(d.infos, toStr("SSH 上传证书成功", nil)) case certFormatJKS: - jksData, err := convertPEMToJKS( + jksData, err := x509.TransformCertificateFromPEMToJKS( d.option.Certificate.Certificate, d.option.Certificate.PrivateKey, d.option.DeployConfig.GetConfigAsString("jksAlias"), diff --git a/internal/pkg/utils/x509/transformer.go b/internal/pkg/utils/x509/transformer.go new file mode 100644 index 00000000..6170d88a --- /dev/null +++ b/internal/pkg/utils/x509/transformer.go @@ -0,0 +1,87 @@ +package x509 + +import ( + "bytes" + "encoding/pem" + "errors" + "time" + + "github.com/pavlo-v-chernykh/keystore-go/v4" + "software.sslmate.com/src/go-pkcs12" +) + +// 将 PEM 编码的证书字符串转换为 PFX 格式。 +// +// 入参: +// - certPem: 证书 PEM 内容。 +// - privkeyPem: 私钥 PEM 内容。 +// - pfxPassword: PFX 导出密码。 +// +// 出参: +// - data: PFX 格式的证书数据。 +// - err: 错误。 +func TransformCertificateFromPEMToPFX(certPem string, privkeyPem string, pfxPassword string) ([]byte, error) { + cert, err := ParseCertificateFromPEM(certPem) + if err != nil { + return nil, err + } + + privkey, err := ParsePKCS1PrivateKeyFromPEM(privkeyPem) + if err != nil { + return nil, err + } + + pfxData, err := pkcs12.LegacyRC2.Encode(privkey, cert, nil, pfxPassword) + if err != nil { + return nil, err + } + + return pfxData, nil +} + +// 将 PEM 编码的证书字符串转换为 JKS 格式。 +// +// 入参: +// - certPem: 证书 PEM 内容。 +// - privkeyPem: 私钥 PEM 内容。 +// - jksAlias: JKS 别名。 +// - jksKeypass: JKS 密钥密码。 +// - jksStorepass: JKS 存储密码。 +// +// 出参: +// - data: JKS 格式的证书数据。 +// - err: 错误。 +func TransformCertificateFromPEMToJKS(certPem string, privkeyPem string, jksAlias string, jksKeypass string, jksStorepass string) ([]byte, error) { + certBlock, _ := pem.Decode([]byte(certPem)) + if certBlock == nil { + return nil, errors.New("failed to decode certificate PEM") + } + + privkeyBlock, _ := pem.Decode([]byte(privkeyPem)) + if privkeyBlock == nil { + return nil, errors.New("failed to decode private key PEM") + } + + ks := keystore.New() + entry := keystore.PrivateKeyEntry{ + CreationTime: time.Now(), + PrivateKey: privkeyBlock.Bytes, + CertificateChain: []keystore.Certificate{ + { + Type: "X509", + Content: certBlock.Bytes, + }, + }, + } + + if err := ks.SetPrivateKeyEntry(jksAlias, entry, []byte(jksKeypass)); err != nil { + return nil, err + } + + var buf bytes.Buffer + if err := ks.Store(&buf, []byte(jksStorepass)); err != nil { + return nil, err + } + + return buf.Bytes(), nil +}