allinssl/backend/public/cert.go

241 lines
6.2 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package public
import (
"bytes"
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/hex"
"encoding/pem"
"errors"
"fmt"
"github.com/pavlo-v-chernykh/keystore-go/v4"
"software.sslmate.com/src/go-pkcs12"
"strings"
"time"
)
// **解析 PEM 格式的证书**
func ParseCertificate(certPEM []byte) (*x509.Certificate, error) {
block, _ := pem.Decode(certPEM)
if block == nil {
return nil, fmt.Errorf("无法解析证书 PEM")
}
return x509.ParseCertificate(block.Bytes)
}
// **解析 PEM 格式的私钥**
func ParsePrivateKey(keyPEM []byte) (crypto.PrivateKey, error) {
block, _ := pem.Decode(keyPEM)
if block == nil {
return nil, fmt.Errorf("无法解析私钥 PEM")
}
// **尝试解析不同类型的私钥**
if key, err := x509.ParsePKCS1PrivateKey(block.Bytes); err == nil {
return key, nil
}
if key, err := x509.ParseECPrivateKey(block.Bytes); err == nil {
return key, nil
}
if key, err := x509.ParsePKCS8PrivateKey(block.Bytes); err == nil {
return key, nil
}
return nil, fmt.Errorf("无法识别的私钥格式")
}
// **检查证书是否过期**
func CheckCertificateExpiration(cert *x509.Certificate) error {
now := time.Now()
if now.Before(cert.NotBefore) {
return fmt.Errorf("证书尚未生效,有效期开始于: %v", cert.NotBefore)
}
if now.After(cert.NotAfter) {
return fmt.Errorf("证书已过期,有效期截止到: %v", cert.NotAfter)
}
return nil
}
// **检查证书是否与私钥匹配**
func VerifyCertificateAndKey(cert *x509.Certificate, privateKey crypto.PrivateKey) error {
messageARR := sha256.Sum256([]byte("test message"))
message := messageARR[:]
var signature []byte
var err error
// **用私钥签名数据**
switch key := privateKey.(type) {
case *rsa.PrivateKey:
signature, err = rsa.SignPKCS1v15(nil, key, crypto.SHA256, message)
case *ecdsa.PrivateKey:
signature, err = key.Sign(rand.Reader, message, crypto.SHA256)
case ed25519.PrivateKey:
signature = ed25519.Sign(key, message)
default:
err = errors.New("不支持的私钥类型")
}
if err != nil {
return fmt.Errorf("签名失败: %v", err)
}
// **使用公钥验证签名**
switch pub := cert.PublicKey.(type) {
case *rsa.PublicKey:
err = rsa.VerifyPKCS1v15(pub, crypto.SHA256, message, signature)
case *ecdsa.PublicKey:
ok := ecdsa.VerifyASN1(pub, message, signature)
if !ok {
err = fmt.Errorf("ECDSA 签名验证失败")
}
case ed25519.PublicKey:
if !ed25519.Verify(pub, message, signature) {
err = fmt.Errorf("Ed25519 签名验证失败")
}
default:
err = fmt.Errorf("不支持的公钥类型: %T", pub)
}
return err
}
// **主验证函数**
func ValidateSSLCertificate(certStr, keyStr string) error {
certPEM, keyPEM := []byte(certStr), []byte(keyStr)
// **解析证书和私钥**
cert, err := ParseCertificate(certPEM)
if err != nil {
return fmt.Errorf("解析证书失败: %v", err)
}
privateKey, err := ParsePrivateKey(keyPEM)
if err != nil {
return fmt.Errorf("解析私钥失败: %v", err)
}
// **检查证书有效期**
if err := CheckCertificateExpiration(cert); err != nil {
return err
}
// **检查证书和私钥是否匹配**
if err := VerifyCertificateAndKey(cert, privateKey); err != nil {
return fmt.Errorf("证书与私钥不匹配: %v", err)
}
return nil
}
// 获取sha256
func GetSHA256(certStr string) (string, error) {
certPEM := []byte(certStr)
block, _ := pem.Decode(certPEM)
if block == nil {
return "", fmt.Errorf("无法解析证书 PEM")
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return "", fmt.Errorf("解析证书失败: %v", err)
}
sha256Hash := sha256.Sum256(cert.Raw)
return hex.EncodeToString(sha256Hash[:]), nil
}
// PEMToPFX 将PEM格式的证书和私钥转换为PFX格式
func PEMToPFX(certPEM, keyPEM, pfxPassword string) ([]byte, error) {
// 使用默认密码如果未提供
if pfxPassword == "" {
pfxPassword = "allinssl"
}
// 解析证书PEM
certBlock, _ := pem.Decode([]byte(certPEM))
if certBlock == nil || certBlock.Type != "CERTIFICATE" {
return nil, fmt.Errorf("无效的证书PEM格式")
}
// 解析私钥PEM
keyBlock, _ := pem.Decode([]byte(keyPEM))
if keyBlock == nil || (!strings.Contains(keyBlock.Type, "PRIVATE KEY")) {
return nil, fmt.Errorf("无效的私钥PEM格式")
}
// 解析X.509证书
cert, err := x509.ParseCertificate(certBlock.Bytes)
if err != nil {
return nil, fmt.Errorf("解析证书失败: %v", err)
}
// 尝试解析私钥(PKCS8或PKCS1格式)
var privKey interface{}
privKey, err = x509.ParsePKCS8PrivateKey(keyBlock.Bytes)
if err != nil {
privKey, err = x509.ParsePKCS1PrivateKey(keyBlock.Bytes)
if err != nil {
return nil, fmt.Errorf("解析私钥失败: %v", err)
}
}
// 编码为PFX格式
pfxData, err := pkcs12.Encode(rand.Reader, privKey, cert, nil, pfxPassword)
if err != nil {
return nil, fmt.Errorf("编码PFX失败: %v", err)
}
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
}