From 7d272a6c524daf05c770161967fc5c1b4d8099ca Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Mon, 7 Jul 2025 17:27:04 +0800 Subject: [PATCH] fix: missing pfx certificiate chains --- pkg/utils/cert/common.go | 16 +++++++++ pkg/utils/cert/extractor.go | 26 +++++++-------- pkg/utils/cert/transformer.go | 62 +++++++++++++++++++++++------------ 3 files changed, 68 insertions(+), 36 deletions(-) diff --git a/pkg/utils/cert/common.go b/pkg/utils/cert/common.go index 56703125..bdc86367 100644 --- a/pkg/utils/cert/common.go +++ b/pkg/utils/cert/common.go @@ -2,6 +2,7 @@ package cert import ( "crypto/x509" + "encoding/pem" ) // 比较两个 x509.Certificate 对象,判断它们是否是同一张证书。 @@ -24,3 +25,18 @@ func EqualCertificate(a, b *x509.Certificate) bool { a.Issuer.SerialNumber == b.Issuer.SerialNumber && a.Subject.SerialNumber == b.Subject.SerialNumber } + +func decodePEM(data []byte) []*pem.Block { + blocks := make([]*pem.Block, 0) + for { + block, rest := pem.Decode(data) + if block == nil { + break + } + + blocks = append(blocks, block) + data = rest + } + + return blocks +} diff --git a/pkg/utils/cert/extractor.go b/pkg/utils/cert/extractor.go index 1e116b1f..b9e4607f 100644 --- a/pkg/utils/cert/extractor.go +++ b/pkg/utils/cert/extractor.go @@ -3,6 +3,7 @@ package cert import ( "encoding/pem" "errors" + "fmt" ) // 从 PEM 编码的证书字符串解析并提取服务器证书和中间证书。 @@ -15,32 +16,27 @@ import ( // - intermediaCertPEM: 中间证书的 PEM 内容。 // - err: 错误。 func ExtractCertificatesFromPEM(certPEM string) (_serverCertPEM string, _intermediaCertPEM string, _err error) { - pemBlocks := make([]*pem.Block, 0) - pemData := []byte(certPEM) - for { - block, rest := pem.Decode(pemData) - if block == nil || block.Type != "CERTIFICATE" { - break + blocks := decodePEM([]byte(certPEM)) + for i, block := range blocks { + if block.Type != "CERTIFICATE" { + return "", "", fmt.Errorf("invalid PEM block type at %d, expected 'CERTIFICATE', got '%s'", i, block.Type) } - - pemBlocks = append(pemBlocks, block) - pemData = rest } serverCertPEM := "" intermediaCertPEM := "" - if len(pemBlocks) == 0 { + if len(blocks) == 0 { return "", "", errors.New("failed to decode PEM block") } - if len(pemBlocks) > 0 { - serverCertPEM = string(pem.EncodeToMemory(pemBlocks[0])) + if len(blocks) > 0 { + serverCertPEM = string(pem.EncodeToMemory(blocks[0])) } - if len(pemBlocks) > 1 { - for i := 1; i < len(pemBlocks); i++ { - intermediaCertPEM += string(pem.EncodeToMemory(pemBlocks[i])) + if len(blocks) > 1 { + for i := 1; i < len(blocks); i++ { + intermediaCertPEM += string(pem.EncodeToMemory(blocks[i])) } } diff --git a/pkg/utils/cert/transformer.go b/pkg/utils/cert/transformer.go index bf467efa..690ae19f 100644 --- a/pkg/utils/cert/transformer.go +++ b/pkg/utils/cert/transformer.go @@ -2,8 +2,9 @@ package cert import ( "bytes" - "encoding/pem" + "crypto/x509" "errors" + "fmt" "time" "github.com/pavlo-v-chernykh/keystore-go/v4" @@ -21,9 +22,19 @@ import ( // - data: PFX 格式的证书数据。 // - err: 错误。 func TransformCertificateFromPEMToPFX(certPEM string, privkeyPEM string, pfxPassword string) ([]byte, error) { - cert, err := ParseCertificateFromPEM(certPEM) - if err != nil { - return nil, err + blocks := decodePEM([]byte(certPEM)) + + certs := make([]*x509.Certificate, 0, len(blocks)) + for i, block := range blocks { + if block.Type != "CERTIFICATE" { + return nil, fmt.Errorf("invalid PEM block type at %d, expected 'CERTIFICATE', got '%s'", i, block.Type) + } + + cert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + return nil, err + } + certs = append(certs, cert) } privkey, err := ParsePrivateKeyFromPEM(privkeyPEM) @@ -31,12 +42,16 @@ func TransformCertificateFromPEMToPFX(certPEM string, privkeyPEM string, pfxPass return nil, err } - pfxData, err := pkcs12.LegacyRC2.Encode(privkey, cert, nil, pfxPassword) - if err != nil { - return nil, err + var pfxData []byte + if len(certs) == 0 { + return nil, errors.New("failed to decode certificate PEM") + } else if len(certs) == 1 { + pfxData, err = pkcs12.Legacy.Encode(privkey, certs[0], nil, pfxPassword) + } else { + pfxData, err = pkcs12.Legacy.Encode(privkey, certs[0], certs[1:], pfxPassword) } - return pfxData, nil + return pfxData, err } // 将 PEM 编码的证书字符串转换为 JKS 格式。 @@ -52,28 +67,33 @@ func TransformCertificateFromPEMToPFX(certPEM string, privkeyPEM string, pfxPass // - 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 { + certBlocks := decodePEM([]byte(certPEM)) + if len(certBlocks) == 0 { return nil, errors.New("failed to decode certificate PEM") } - privkeyBlock, _ := pem.Decode([]byte(privkeyPEM)) - if privkeyBlock == nil { + privkeyBlocks := decodePEM([]byte(privkeyPEM)) + if len(privkeyBlocks) == 0 { 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, - }, - }, + CreationTime: time.Now(), + PrivateKey: privkeyBlocks[0].Bytes, + CertificateChain: make([]keystore.Certificate, len(certBlocks)), + } + for i, certBlock := range certBlocks { + if certBlock.Type != "CERTIFICATE" { + return nil, fmt.Errorf("invalid PEM block type at %d, expected 'CERTIFICATE', got '%s'", i, certBlock.Type) + } + + entry.CertificateChain[i] = keystore.Certificate{ + Type: "X509", + Content: certBlock.Bytes, + } } + ks := keystore.New() if err := ks.SetPrivateKeyEntry(jksAlias, entry, []byte(jksKeypass)); err != nil { return nil, err }