Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

crypto/x509 - X.509 证书和密钥处理

概述

crypto/x509 包实现了 X.509 证书和密钥的解析和创建功能。

X.509 证书是公钥基础设施(PKI)的核心组件,用于:

  • 🔐 身份验证:验证实体身份
  • 🔑 公钥分发:安全地分发公钥
  • ✍️ 数字签名:提供数字签名验证
  • 🔗 信任链:建立证书信任链

主要用途

  • 📜 证书解析:读取和验证证书
  • 🏭 证书生成:创建证书和 CSR
  • 🔐 密钥管理:解析和编码密钥
  • 🌳 证书链验证:验证证书信任链
  • 🏢 CA 操作:证书颁发机构功能

核心类型

1. Certificate - X.509 证书

type Certificate struct {
    // 基本信息
    Raw                     []byte // 完整原始证书
    RawTBSCertificate       []byte // 待签名证书部分
    RawSubjectPublicKeyInfo []byte // 原始公钥信息
    RawSubject, RawIssuer   []byte // 原始主题和颁发者
    
    // 证书内容
    Signature          []byte
    SignatureAlgorithm SignatureAlgorithm
    
    // 公钥信息
    PublicKey          interface{}
    PublicKeyAlgorithm PublicKeyAlgorithm
    
    // 序列号和有效期
    SerialNumber       *big.Int
    NotBefore, NotAfter time.Time
    
    // 主题和颁发者
    Subject, Issuer   pkix.Name
    
    // 扩展
    Extensions        []pkix.Extension
    ExtraExtensions   []pkix.Extension
    
    // 用途限制
    KeyUsage          KeyUsage
    ExtKeyUsage       []ExtKeyUsage
    UnknownExtKeyUsage []asn1.ObjectIdentifier
    
    // 名称限制
    DNSNames          []string
    EmailAddresses    []string
    IPAddresses       []net.IP
    URIs              []*url.URL
    
    // CRL 和 OCSP
    CRLDistributionPoints []string
    OCSPServer            []string
    IssuingCertificateURL []string
    
    // 策略
    Policies              []asn1.ObjectIdentifier
    
    // 证书链
    Candidates []Certificate
}

重要字段说明

证书标识

  • SerialNumber:证书序列号(唯一标识)
  • Subject:证书持有者信息
  • Issuer:证书颁发者信息
  • NotBefore / NotAfter:有效期

公钥信息

  • PublicKey:公钥(*rsa.PublicKey*ecdsa.PublicKey 等)
  • PublicKeyAlgorithm:公钥算法(RSA、ECDSA 等)
  • SignatureAlgorithm:签名算法

用途限制

  • KeyUsage:密钥用途(数字签名、密钥加密等)
  • ExtKeyUsage:扩展密钥用途(服务器认证、客户端认证等)

名称信息

  • DNSNames:允许的域名(SAN 扩展)
  • EmailAddresses:允许的邮箱地址
  • IPAddresses:允许的 IP 地址

2. CertificateRequest - 证书签名请求(CSR)

type CertificateRequest struct {
    Raw                []byte
    RawTBSCertificateRequest []byte
    RawSubjectPublicKeyInfo  []byte
    SignatureAlgorithm SignatureAlgorithm
    
    // 主题信息
    Subject pkix.Name
    
    // 公钥
    PublicKey interface{}
    PublicKeyAlgorithm PublicKeyAlgorithm
    
    // 扩展
    Attributes []pkix.AttributeTypeAndValueSET
    Extensions []pkix.Extension
    
    // 名称信息
    DNSNames       []string
    EmailAddresses []string
    IPAddresses    []net.IP
    URIs           []*url.URL
}

3. CertPool - 证书池

type CertPool struct {
    // 包含过滤或未导出的字段
}

功能:存储一组证书,用于证书验证。

主要方法

// 创建空证书池
func NewCertPool() *CertPool

// 添加证书
func (p *CertPool) AppendCertsFromPEM(pemCerts []byte) bool

// 添加系统证书
func (p *CertPool) AddCert(cert *Certificate)

// 获取证书主题列表
func (p *CertPool) Subjects() [][]byte

4. SignatureAlgorithm - 签名算法

type SignatureAlgorithm int

const (
    // 未知
    UnknownSignatureAlgorithm SignatureAlgorithm = iota
    
    // MD5 系(已弃用)
    MD2WithRSA
    MD5WithRSA
    
    // SHA-1 系(不推荐)
    SHA1WithRSA
    
    // SHA-256 系(推荐)
    SHA256WithRSA
    SHA384WithRSA
    SHA512WithRSA
    
    // ECDSA 系(推荐)
    ECDSAWithSHA1
    ECDSAWithSHA256
    ECDSAWithSHA384
    ECDSAWithSHA512
    
    // Ed25519(推荐)
    PureEd25519
)

5. PublicKeyAlgorithm - 公钥算法

type PublicKeyAlgorithm int

const (
    UnknownPublicKeyAlgorithm PublicKeyAlgorithm = iota
    RSA
    DSA
    EC
    Ed25519
)

6. KeyUsage - 密钥用途

type KeyUsage int

const (
    KeyUsageDigitalSignature KeyUsage = 1 << iota
    KeyUsageContentCommitment
    KeyUsageKeyEncipherment
    KeyUsageDataEncipherment
    KeyUsageKeyAgreement
    KeyUsageCertSign          // 证书签名
    KeyUsageCRLSign           // CRL 签名
    KeyUsageEncipherOnly
    KeyUsageDecipherOnly
)

7. ExtKeyUsage - 扩展密钥用途

type ExtKeyUsage int

const (
    ExtKeyUsageAny ExtKeyUsage = iota
    ExtKeyUsageServerAuth           // 服务器认证
    ExtKeyUsageClientAuth           // 客户端认证
    ExtKeyUsageCodeSigning          // 代码签名
    ExtKeyUsageEmailProtection      // 邮件保护
    ExtKeyUsageIPSecEndSystem       // IPSec
    ExtKeyUsageIPSecTunnel          // IPSec 隧道
    ExtKeyUsageIPSecUser            // IPSec 用户
    ExtKeyUsageTimeStamping         // 时间戳
    ExtKeyUsageOCSPSigning          // OCSP 签名
    ExtKeyUsageMicrosoftServerGatedCrypto
    ExtKeyUsageNetscapeServerGatedCrypto
    ExtKeyUsageMicrosoftCommercialCodeSigning
    ExtKeyUsageMicrosoftKernelCodeSigning
)

证书解析

示例 1:解析 PEM 编码证书

package main

import (
    "crypto/x509"
    "encoding/pem"
    "fmt"
    "io/ioutil"
    "log"
    "time"
)

func main() {
    // 1. 读取证书文件
    certPEM, err := ioutil.ReadFile("certificate.pem")
    if err != nil {
        log.Fatal(err)
    }
    
    // 2. 解码 PEM
    block, _ := pem.Decode(certPEM)
    if block == nil {
        log.Fatal("无法解析 PEM")
    }
    
    // 3. 解析证书
    cert, err := x509.ParseCertificate(block.Bytes)
    if err != nil {
        log.Fatal(err)
    }
    
    // 4. 显示证书信息
    fmt.Printf("版本:%d\n", cert.Version)
    fmt.Printf("序列号:%s\n", cert.SerialNumber)
    fmt.Printf("主题:%s\n", cert.Subject.CommonName)
    fmt.Printf("颁发者:%s\n", cert.Issuer.CommonName)
    fmt.Printf("有效期:%s - %s\n", cert.NotBefore, cert.NotAfter)
    fmt.Printf("公钥算法:%v\n", cert.PublicKeyAlgorithm)
    fmt.Printf("签名算法:%v\n", cert.SignatureAlgorithm)
    
    // 5. 检查有效期
    now := time.Now()
    if now.Before(cert.NotBefore) {
        fmt.Println("⚠️ 证书尚未生效")
    } else if now.After(cert.NotAfter) {
        fmt.Println("❌ 证书已过期")
    } else {
        fmt.Println("✅ 证书有效")
    }
    
    // 6. 显示 SAN(主题备用名称)
    if len(cert.DNSNames) > 0 {
        fmt.Printf("DNS 名称:%v\n", cert.DNSNames)
    }
    if len(cert.IPAddresses) > 0 {
        fmt.Printf("IP 地址:%v\n", cert.IPAddresses)
    }
}

示例 2:解析 DER 编码证书

package main

import (
    "crypto/x509"
    "fmt"
    "io/ioutil"
    "log"
)

func main() {
    // 1. 读取 DER 编码证书
    certDER, err := ioutil.ReadFile("certificate.der")
    if err != nil {
        log.Fatal(err)
    }
    
    // 2. 直接解析 DER
    cert, err := x509.ParseCertificate(certDER)
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("证书主题:%s\n", cert.Subject.CommonName)
    fmt.Printf("证书颁发者:%s\n", cert.Issuer.CommonName)
}

示例 3:解析证书链

package main

import (
    "crypto/x509"
    "encoding/pem"
    "fmt"
    "io/ioutil"
    "log"
)

// ParseCertificateChain 解析证书链
func ParseCertificateChain(pemData []byte) ([]*x509.Certificate, error) {
    var certs []*x509.Certificate
    
    for len(pemData) > 0 {
        var block *pem.Block
        block, pemData = pem.Decode(pemData)
        if block == nil {
            break
        }
        
        if block.Type != "CERTIFICATE" {
            continue
        }
        
        cert, err := x509.ParseCertificate(block.Bytes)
        if err != nil {
            return nil, err
        }
        
        certs = append(certs, cert)
    }
    
    return certs, nil
}

func main() {
    // 1. 读取证书链文件
    chainPEM, err := ioutil.ReadFile("chain.pem")
    if err != nil {
        log.Fatal(err)
    }
    
    // 2. 解析证书链
    certs, err := ParseCertificateChain(chainPEM)
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("证书链长度:%d\n", len(certs))
    
    // 3. 显示每个证书的信息
    for i, cert := range certs {
        fmt.Printf("\n证书 %d:\n", i+1)
        fmt.Printf("  主题:%s\n", cert.Subject.CommonName)
        fmt.Printf("  颁发者:%s\n", cert.Issuer.CommonName)
        
        if i == 0 {
            fmt.Println("  (终端实体证书)")
        } else if i == len(certs)-1 {
            fmt.Println("  (根证书或中间证书)")
        } else {
            fmt.Println("  (中间证书)")
        }
    }
}

证书生成

示例 4:生成自签名证书

package main

import (
    "crypto/ecdsa"
    "crypto/elliptic"
    "crypto/rand"
    "crypto/x509"
    "crypto/x509/pkix"
    "encoding/pem"
    "fmt"
    "log"
    "math/big"
    "net"
    "os"
    "time"
)

func main() {
    // 1. 生成私钥(ECDSA P-256)
    priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
    if err != nil {
        log.Fatal(err)
    }
    
    // 2. 创建证书模板
    serialNumber, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
    if err != nil {
        log.Fatal(err)
    }
    
    template := x509.Certificate{
        SerialNumber: serialNumber,
        Subject: pkix.Name{
            Country:            []string{"CN"},
            Province:           []string{"Beijing"},
            Locality:           []string{"Beijing"},
            Organization:       []string{"My Organization"},
            OrganizationalUnit: []string{"IT Department"},
            CommonName:         "localhost",
        },
        NotBefore: time.Now(),
        NotAfter:  time.Now().Add(365 * 24 * time.Hour), // 1 年有效期
        
        // 密钥用途
        KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
        
        // 扩展密钥用途(服务器认证)
        ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
        
        // 基本约束
        BasicConstraintsValid: true,
        
        // 主题备用名称
        DNSNames: []string{
            "localhost",
            "example.com",
            "*.example.com",
        },
        IPAddresses: []net.IP{
            net.ParseIP("127.0.0.1"),
            net.ParseIP("::1"),
        },
    }
    
    // 3. 创建证书(自签名)
    derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
    if err != nil {
        log.Fatal(err)
    }
    
    // 4. 保存证书
    certFile, err := os.Create("server.crt")
    if err != nil {
        log.Fatal(err)
    }
    defer certFile.Close()
    
    err = pem.Encode(certFile, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
    if err != nil {
        log.Fatal(err)
    }
    
    // 5. 保存私钥
    keyFile, err := os.Create("server.key")
    if err != nil {
        log.Fatal(err)
    }
    defer keyFile.Close()
    
    privBytes, err := x509.MarshalECPrivateKey(priv)
    if err != nil {
        log.Fatal(err)
    }
    
    err = pem.Encode(keyFile, &pem.Block{Type: "EC PRIVATE KEY", Bytes: privBytes})
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Println("✓ 自签名证书生成成功")
    fmt.Println("  证书:server.crt")
    fmt.Println("  私钥:server.key")
}

示例 5:生成 RSA 证书

package main

import (
    "crypto/rand"
    "crypto/rsa"
    "crypto/x509"
    "crypto/x509/pkix"
    "encoding/pem"
    "fmt"
    "log"
    "math/big"
    "os"
    "time"
)

func main() {
    // 1. 生成 RSA 私钥(2048 位)
    priv, err := rsa.GenerateKey(rand.Reader, 2048)
    if err != nil {
        log.Fatal(err)
    }
    
    // 2. 创建证书模板
    serialNumber, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
    if err != nil {
        log.Fatal(err)
    }
    
    template := x509.Certificate{
        SerialNumber: serialNumber,
        Subject: pkix.Name{
            CommonName:   "example.com",
            Organization: []string{"Example Inc"},
        },
        NotBefore: time.Now(),
        NotAfter:  time.Now().Add(365 * 24 * time.Hour),
        
        KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
        ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
        BasicConstraintsValid: true,
        DNSNames:              []string{"example.com", "www.example.com"},
    }
    
    // 3. 创建证书
    derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
    if err != nil {
        log.Fatal(err)
    }
    
    // 4. 保存证书
    certFile, err := os.Create("rsa-cert.pem")
    if err != nil {
        log.Fatal(err)
    }
    defer certFile.Close()
    
    pem.Encode(certFile, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
    
    // 5. 保存私钥
    keyFile, err := os.Create("rsa-key.pem")
    if err != nil {
        log.Fatal(err)
    }
    defer keyFile.Close()
    
    privBytes := x509.MarshalPKCS1PrivateKey(priv)
    pem.Encode(keyFile, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: privBytes})
    
    fmt.Println("✓ RSA 证书生成成功")
}

示例 6:生成 CA 证书

package main

import (
    "crypto/ecdsa"
    "crypto/elliptic"
    "crypto/rand"
    "crypto/x509"
    "crypto/x509/pkix"
    "encoding/pem"
    "fmt"
    "log"
    "math/big"
    "os"
    "time"
)

func main() {
    // 1. 生成 CA 私钥
    priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
    if err != nil {
        log.Fatal(err)
    }
    
    // 2. 创建 CA 证书模板
    serialNumber, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
    if err != nil {
        log.Fatal(err)
    }
    
    template := x509.Certificate{
        SerialNumber: serialNumber,
        Subject: pkix.Name{
            Country:      []string{"CN"},
            Organization: []string{"My CA"},
            CommonName:   "My Root CA",
        },
        NotBefore: time.Now(),
        NotAfter:  time.Now().Add(10 * 365 * 24 * time.Hour), // 10 年
        
        // CA 密钥用途
        KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
        
        // 基本约束(CA 证书)
        BasicConstraintsValid: true,
        IsCA:                  true,
        MaxPathLen:            1, // 允许一级中间 CA
        
        // 主题密钥标识符
        SubjectKeyId: []byte{1, 2, 3, 4, 6},
    }
    
    // 3. 创建 CA 证书(自签名)
    derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
    if err != nil {
        log.Fatal(err)
    }
    
    // 4. 保存 CA 证书
    caFile, err := os.Create("ca.crt")
    if err != nil {
        log.Fatal(err)
    }
    defer caFile.Close()
    
    pem.Encode(caFile, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
    
    // 5. 保存 CA 私钥
    caKeyFile, err := os.Create("ca.key")
    if err != nil {
        log.Fatal(err)
    }
    defer caKeyFile.Close()
    
    privBytes, err := x509.MarshalECPrivateKey(priv)
    if err != nil {
        log.Fatal(err)
    }
    
    pem.Encode(caKeyFile, &pem.Block{Type: "EC PRIVATE KEY", Bytes: privBytes})
    
    fmt.Println("✓ CA 证书生成成功")
    fmt.Println("  CA 证书:ca.crt")
    fmt.Println("  CA 私钥:ca.key(妥善保管!)")
}

示例 7:使用 CA 签发证书

package main

import (
    "crypto/ecdsa"
    "crypto/elliptic"
    "crypto/rand"
    "crypto/x509"
    "crypto/x509/pkix"
    "encoding/pem"
    "fmt"
    "io/ioutil"
    "log"
    "math/big"
    "os"
    "time"
)

// loadCA 加载 CA 证书和私钥
func loadCA(caCertPath, caKeyPath string) (*x509.Certificate, *ecdsa.PrivateKey, error) {
    // 加载 CA 证书
    caCertPEM, err := ioutil.ReadFile(caCertPath)
    if err != nil {
        return nil, nil, err
    }
    
    block, _ := pem.Decode(caCertPEM)
    if block == nil {
        return nil, nil, fmt.Errorf("无法解析 CA 证书")
    }
    
    caCert, err := x509.ParseCertificate(block.Bytes)
    if err != nil {
        return nil, nil, err
    }
    
    // 加载 CA 私钥
    caKeyPEM, err := ioutil.ReadFile(caKeyPath)
    if err != nil {
        return nil, nil, err
    }
    
    block, _ = pem.Decode(caKeyPEM)
    if block == nil {
        return nil, nil, fmt.Errorf("无法解析 CA 私钥")
    }
    
    caKey, err := x509.ParseECPrivateKey(block.Bytes)
    if err != nil {
        return nil, nil, err
    }
    
    return caCert, caKey, nil
}

// signCertificate 使用 CA 签发证书
func signCertificate(caCert *x509.Certificate, caKey *ecdsa.PrivateKey, domain string) error {
    // 1. 生成服务器私钥
    priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
    if err != nil {
        return err
    }
    
    // 2. 创建证书模板
    serialNumber, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
    if err != nil {
        return err
    }
    
    template := x509.Certificate{
        SerialNumber: serialNumber,
        Subject: pkix.Name{
            CommonName:   domain,
            Organization: []string{"Example Inc"},
        },
        NotBefore: time.Now(),
        NotAfter:  time.Now().Add(365 * 24 * time.Hour),
        
        KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
        ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
        BasicConstraintsValid: true,
        DNSNames:              []string{domain},
    }
    
    // 3. 使用 CA 签名
    derBytes, err := x509.CreateCertificate(rand.Reader, &template, caCert, &priv.PublicKey, caKey)
    if err != nil {
        return err
    }
    
    // 4. 保存证书
    certFile, err := os.Create(domain + ".crt")
    if err != nil {
        return err
    }
    defer certFile.Close()
    
    pem.Encode(certFile, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
    
    // 5. 保存私钥
    keyFile, err := os.Create(domain + ".key")
    if err != nil {
        return err
    }
    defer keyFile.Close()
    
    privBytes, err := x509.MarshalECPrivateKey(priv)
    if err != nil {
        return err
    }
    
    pem.Encode(keyFile, &pem.Block{Type: "EC PRIVATE KEY", Bytes: privBytes})
    
    return nil
}

func main() {
    // 1. 加载 CA
    caCert, caKey, err := loadCA("ca.crt", "ca.key")
    if err != nil {
        log.Fatal(err)
    }
    
    // 2. 签发证书
    domain := "example.com"
    err = signCertificate(caCert, caKey, domain)
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("✓ 证书签发成功:%s\n", domain)
    fmt.Printf("  证书:%s.crt\n", domain)
    fmt.Printf("  私钥:%s.key\n", domain)
    fmt.Printf("  颁发者:%s\n", caCert.Subject.CommonName)
}

证书签名请求(CSR)

示例 8:生成 CSR

package main

import (
    "crypto/ecdsa"
    "crypto/elliptic"
    "crypto/rand"
    "crypto/x509"
    "crypto/x509/pkix"
    "encoding/pem"
    "fmt"
    "log"
    "os"
)

func main() {
    // 1. 生成私钥
    priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
    if err != nil {
        log.Fatal(err)
    }
    
    // 2. 创建 CSR 模板
    template := &x509.CertificateRequest{
        Subject: pkix.Name{
            Country:            []string{"CN"},
            Province:           []string{"Beijing"},
            Locality:           []string{"Beijing"},
            Organization:       []string{"Example Inc"},
            OrganizationalUnit: []string{"IT Department"},
            CommonName:         "example.com",
        },
        // 主题备用名称
        DNSNames: []string{
            "example.com",
            "www.example.com",
            "api.example.com",
        },
        // 额外属性(可选)
        ExtraExtensions: []pkix.Extension{
            {
                Id:    asn1.ObjectIdentifier{1, 2, 840, 113549, 1, 9, 1}, // Email
                Value: []byte("admin@example.com"),
            },
        },
    }
    
    // 3. 创建 CSR
    csrDER, err := x509.CreateCertificateRequest(rand.Reader, template, priv)
    if err != nil {
        log.Fatal(err)
    }
    
    // 4. 保存 CSR
    csrFile, err := os.Create("example.csr")
    if err != nil {
        log.Fatal(err)
    }
    defer csrFile.Close()
    
    pem.Encode(csrFile, &pem.Block{Type: "CERTIFICATE REQUEST", Bytes: csrDER})
    
    // 5. 保存私钥(可选,通常与 CSR 一起保存)
    keyFile, err := os.Create("example.key")
    if err != nil {
        log.Fatal(err)
    }
    defer keyFile.Close()
    
    privBytes, err := x509.MarshalECPrivateKey(priv)
    if err != nil {
        log.Fatal(err)
    }
    
    pem.Encode(keyFile, &pem.Block{Type: "EC PRIVATE KEY", Bytes: privBytes})
    
    fmt.Println("✓ CSR 生成成功")
    fmt.Println("  CSR 文件:example.csr")
    fmt.Println("  私钥文件:example.key")
    fmt.Println("\n下一步:将 CSR 提交给 CA 签发证书")
}

示例 9:解析和验证 CSR

package main

import (
    "crypto/x509"
    "encoding/pem"
    "fmt"
    "io/ioutil"
    "log"
)

func main() {
    // 1. 读取 CSR 文件
    csrPEM, err := ioutil.ReadFile("example.csr")
    if err != nil {
        log.Fatal(err)
    }
    
    // 2. 解码 PEM
    block, _ := pem.Decode(csrPEM)
    if block == nil {
        log.Fatal("无法解析 CSR PEM")
    }
    
    // 3. 解析 CSR
    csr, err := x509.ParseCertificateRequest(block.Bytes)
    if err != nil {
        log.Fatal(err)
    }
    
    // 4. 验证 CSR 签名
    err = csr.CheckSignature()
    if err != nil {
        log.Fatal("CSR 签名验证失败:", err)
    }
    
    // 5. 显示 CSR 信息
    fmt.Println("✓ CSR 验证成功")
    fmt.Printf("主题:%s\n", csr.Subject.CommonName)
    fmt.Printf("组织:%s\n", csr.Subject.Organization)
    fmt.Printf("公钥算法:%v\n", csr.PublicKeyAlgorithm)
    fmt.Printf("DNS 名称:%v\n", csr.DNSNames)
    
    // 6. 检查签名算法
    fmt.Printf("签名算法:%v\n", csr.SignatureAlgorithm)
}

证书验证

示例 10:证书链验证

package main

import (
    "crypto/x509"
    "encoding/pem"
    "fmt"
    "io/ioutil"
    "log"
    "time"
)

func main() {
    // 1. 加载证书
    certPEM, err := ioutil.ReadFile("server.crt")
    if err != nil {
        log.Fatal(err)
    }
    
    block, _ := pem.Decode(certPEM)
    if block == nil {
        log.Fatal("无法解析证书")
    }
    
    cert, err := x509.ParseCertificate(block.Bytes)
    if err != nil {
        log.Fatal(err)
    }
    
    // 2. 加载 CA 证书
    caPEM, err := ioutil.ReadFile("ca.crt")
    if err != nil {
        log.Fatal(err)
    }
    
    caBlock, _ := pem.Decode(caPEM)
    if caBlock == nil {
        log.Fatal("无法解析 CA 证书")
    }
    
    caCert, err := x509.ParseCertificate(caBlock.Bytes)
    if err != nil {
        log.Fatal(err)
    }
    
    // 3. 创建证书池
    roots := x509.NewCertPool()
    roots.AddCert(caCert)
    
    // 4. 创建验证选项
    opts := x509.VerifyOptions{
        Roots:       roots,
        CurrentTime: time.Now(),
        KeyUsages:   []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
    }
    
    // 5. 验证证书
    chains, err := cert.Verify(opts)
    if err != nil {
        log.Fatal("证书验证失败:", err)
    }
    
    fmt.Println("✓ 证书验证成功")
    fmt.Printf("找到 %d 条信任链\n", len(chains))
    
    // 6. 显示证书链信息
    for i, chain := range chains {
        fmt.Printf("\n信任链 %d:\n", i+1)
        for j, cert := range chain {
            fmt.Printf("  %d. %s\n", j+1, cert.Subject.CommonName)
        }
    }
}

示例 11:证书吊销检查(CRL)

package main

import (
    "crypto/x509"
    "crypto/x509/pkix"
    "encoding/pem"
    "fmt"
    "io/ioutil"
    "log"
    "time"
)

// loadCRL 加载 CRL 文件
func loadCRL(crlPath string) (*pkix.CertificateList, error) {
    crlPEM, err := ioutil.ReadFile(crlPath)
    if err != nil {
        return nil, err
    }
    
    block, _ := pem.Decode(crlPEM)
    if block == nil {
        return nil, fmt.Errorf("无法解析 CRL PEM")
    }
    
    return x509.ParseCRL(block.Bytes)
}

// isRevoked 检查证书是否被吊销
func isRevoked(cert *x509.Certificate, crl *pkix.CertificateList) bool {
    for _, revoked := range crl.TBSCertList.RevokedCertificates {
        if cert.SerialNumber.Cmp(revoked.SerialNumber) == 0 {
            return true
        }
    }
    return false
}

func main() {
    // 1. 加载证书
    certPEM, err := ioutil.ReadFile("server.crt")
    if err != nil {
        log.Fatal(err)
    }
    
    block, _ := pem.Decode(certPEM)
    cert, err := x509.ParseCertificate(block.Bytes)
    if err != nil {
        log.Fatal(err)
    }
    
    // 2. 加载 CRL
    crl, err := loadCRL("ca.crl")
    if err != nil {
        log.Fatal(err)
    }
    
    // 3. 检查吊销状态
    if isRevoked(cert, crl) {
        fmt.Println("❌ 证书已被吊销")
    } else {
        fmt.Println("✅ 证书未被吊销")
    }
    
    // 4. 显示 CRL 信息
    fmt.Printf("CRL 颁发者:%s\n", crl.TBSCertList.Issuer)
    fmt.Printf("CRL 更新时间:%s\n", crl.TBSCertList.ThisUpdate)
    if crl.TBSCertList.NextUpdate != nil {
        fmt.Printf("CRL 下次更新:%s\n", *crl.TBSCertList.NextUpdate)
    }
    fmt.Printf("吊销证书数量:%d\n", len(crl.TBSCertList.RevokedCertificates))
}

密钥管理

示例 12:解析和编码密钥

package main

import (
    "crypto/ecdsa"
    "crypto/rsa"
    "crypto/x509"
    "encoding/pem"
    "fmt"
    "io/ioutil"
    "log"
)

// ParsePrivateKey 解析私钥
func ParsePrivateKey(keyPEM []byte) (interface{}, error) {
    block, _ := pem.Decode(keyPEM)
    if block == nil {
        return nil, fmt.Errorf("无法解析密钥 PEM")
    }
    
    // 尝试 PKCS#8
    key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
    if err == nil {
        return key, nil
    }
    
    // 尝试 PKCS#1 RSA
    key, err = x509.ParsePKCS1PrivateKey(block.Bytes)
    if err == nil {
        return key, nil
    }
    
    // 尝试 EC 私钥
    key, err = x509.ParseECPrivateKey(block.Bytes)
    if err == nil {
        return key, nil
    }
    
    return nil, fmt.Errorf("无法解析私钥:%v", err)
}

// EncodePrivateKey 编码私钥
func EncodePrivateKey(key interface{}) ([]byte, error) {
    var privBytes []byte
    var err error
    
    switch k := key.(type) {
    case *rsa.PrivateKey:
        privBytes = x509.MarshalPKCS1PrivateKey(k)
        return pem.EncodeToMemory(&pem.Block{
            Type:  "RSA PRIVATE KEY",
            Bytes: privBytes,
        }), nil
        
    case *ecdsa.PrivateKey:
        privBytes, err = x509.MarshalECPrivateKey(k)
        if err != nil {
            return nil, err
        }
        return pem.EncodeToMemory(&pem.Block{
            Type:  "EC PRIVATE KEY",
            Bytes: privBytes,
        }), nil
        
    default:
        return nil, fmt.Errorf("不支持的密钥类型")
    }
}

func main() {
    // 示例:加载和编码密钥
    keyPEM, err := ioutil.ReadFile("server.key")
    if err != nil {
        log.Fatal(err)
    }
    
    // 解析密钥
    key, err := ParsePrivateKey(keyPEM)
    if err != nil {
        log.Fatal(err)
    }
    
    // 显示密钥信息
    switch k := key.(type) {
    case *rsa.PrivateKey:
        fmt.Printf("RSA 私钥,%d 位\n", k.N.BitLen())
    case *ecdsa.PrivateKey:
        fmt.Printf("ECDSA 私钥,曲线:%s\n", k.Curve.Params().Name)
    }
    
    // 重新编码
    encoded, err := EncodePrivateKey(key)
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("编码后的密钥长度:%d 字节\n", len(encoded))
}

示例 13:加密和解密私钥

package main

import (
    "crypto/ecdsa"
    "crypto/elliptic"
    "crypto/rand"
    "crypto/x509"
    "crypto/x509/pkix"
    "encoding/pem"
    "fmt"
    "log"
    "os"
)

func main() {
    password := []byte("my-secret-password")
    
    // 1. 生成密钥
    priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
    if err != nil {
        log.Fatal(err)
    }
    
    // 2. 编码私钥
    privBytes, err := x509.MarshalECPrivateKey(priv)
    if err != nil {
        log.Fatal(err)
    }
    
    // 3. 加密私钥(使用密码)
    encryptedBlock, err := x509.EncryptPEMBlock(
        rand.Reader,
        "ENCRYPTED PRIVATE KEY",
        privBytes,
        password,
        x509.PEMCipherAES256,
    )
    if err != nil {
        log.Fatal(err)
    }
    
    // 4. 保存加密的私钥
    encFile, err := os.Create("encrypted.key")
    if err != nil {
        log.Fatal(err)
    }
    defer encFile.Close()
    
    pem.Encode(encFile, encryptedBlock)
    fmt.Println("✓ 加密的私钥已保存")
    
    // 5. 加载和解密私钥
    encPEM, err := os.ReadFile("encrypted.key")
    if err != nil {
        log.Fatal(err)
    }
    
    block, _ := pem.Decode(encPEM)
    
    // 解密
    decryptedBytes, err := x509.DecryptPEMBlock(block, password)
    if err != nil {
        log.Fatal(err)
    }
    
    // 解析解密后的密钥
    decryptedKey, err := x509.ParseECPrivateKey(decryptedBytes)
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("✓ 私钥解密成功\n")
    fmt.Printf("  曲线:%s\n", decryptedKey.Curve.Params().Name)
}

证书验证选项

示例 14:高级证书验证

package main

import (
    "crypto/x509"
    "encoding/pem"
    "fmt"
    "io/ioutil"
    "log"
    "net"
    "time"
)

func main() {
    // 1. 加载证书
    certPEM, err := ioutil.ReadFile("server.crt")
    if err != nil {
        log.Fatal(err)
    }
    
    block, _ := pem.Decode(certPEM)
    cert, err := x509.ParseCertificate(block.Bytes)
    if err != nil {
        log.Fatal(err)
    }
    
    // 2. 加载 CA 证书
    caPEM, err := ioutil.ReadFile("ca.crt")
    if err != nil {
        log.Fatal(err)
    }
    
    caBlock, _ := pem.Decode(caPEM)
    caCert, err := x509.ParseCertificate(caBlock.Bytes)
    if err != nil {
        log.Fatal(err)
    }
    
    // 3. 创建证书池
    roots := x509.NewCertPool()
    roots.AddCert(caCert)
    
    // 4. 配置验证选项
    opts := x509.VerifyOptions{
        Roots:         roots,
        CurrentTime:   time.Now(),
        DNSName:       "example.com", // 验证域名
        Intermediates: x509.NewCertPool(),
        KeyUsages: []x509.ExtKeyUsage{
            x509.ExtKeyUsageServerAuth,
        },
    }
    
    // 5. 验证证书
    chains, err := cert.Verify(opts)
    if err != nil {
        log.Fatal("证书验证失败:", err)
    }
    
    fmt.Println("✅ 证书验证成功")
    
    // 6. 验证 IP 地址
    opts2 := opts
    opts2.DNSName = "" // 清除 DNS 名称
    opts2.IPAddress = net.ParseIP("192.168.1.1")
    
    // 验证 IP 地址证书
    // chains, err = cert.Verify(opts2)
}

安全最佳实践

✅ 推荐做法

  1. 使用强密钥

    // ✅ ECDSA P-256 或更高
    priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
    
    // ✅ RSA 2048+
    priv, err := rsa.GenerateKey(rand.Reader, 2048)
    
  2. 使用安全的签名算法

    // ✅ 推荐
    SHA256WithRSA
    SHA384WithRSA
    SHA512WithRSA
    ECDSAWithSHA256
    ECDSAWithSHA384
    ECDSAWithSHA512
    PureEd25519
    
  3. 设置合适的密钥用途

    // 服务器证书
    KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
    ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
    
    // CA 证书
    KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
    IsCA: true,
    
  4. 实现证书监控

    // 检查证书有效期
    func checkCertExpiry(certPath string) error {
        cert, err := loadCertificate(certPath)
        if err != nil {
            return err
        }
        
        now := time.Now()
        if now.After(cert.NotAfter) {
            return fmt.Errorf("证书已过期")
        }
        
        // 提前 30 天警告
        if cert.NotAfter.Sub(now) < 30*24*time.Hour {
            log.Printf("警告:证书将在 30 天内过期")
        }
        
        return nil
    }
    
  5. 保护私钥

    // ✅ 使用密码加密私钥
    encryptedBlock, err := x509.EncryptPEMBlock(
        rand.Reader,
        "ENCRYPTED PRIVATE KEY",
        privBytes,
        password,
        x509.PEMCipherAES256,
    )
    
    // ✅ 设置合适的文件权限
    os.Chmod("private.key", 0600)
    

❌ 不安全做法

  1. 不要使用弱签名算法

    // ❌ 避免
    MD5WithRSA    // 已攻破
    SHA1WithRSA   // 不推荐
    ECDSAWithSHA1 // 不推荐
    
  2. 不要使用过短的密钥

    // ❌ 避免
    rsa.GenerateKey(rand.Reader, 1024) // 太短
    
  3. 不要硬编码私钥

    // ❌ 绝对不要
    privateKey := "-----BEGIN PRIVATE KEY-----\n..."
    

总结

核心 API

// 证书解析
ParseCertificate(der []byte) (*Certificate, error)
ParseCertificateRequest(der []byte) (*CertificateRequest, error)

// 证书生成
CreateCertificate(rand io.Reader, template, parent *Certificate, 
                   pub, priv interface{}) ([]byte, error)
CreateCertificateRequest(rand io.Reader, template *CertificateRequest, 
                         priv interface{}) ([]byte, error)

// 证书池
NewCertPool() *CertPool
(p *CertPool) AppendCertsFromPEM(pemCerts []byte) bool
(p *CertPool) AddCert(cert *Certificate)

// 证书验证
(cert *Certificate) Verify(opts VerifyOptions) ([][]*Certificate, error)

// 密钥管理
MarshalPKCS1PrivateKey(key *rsa.PrivateKey) []byte
MarshalECPrivateKey(key *ecdsa.PrivateKey) ([]byte, error)
ParsePKCS8PrivateKey(der []byte) (key interface{}, err error)

使用场景

场景推荐方法说明
解析证书ParseCertificatePEM/DER 解码后解析
生成自签名证书CreateCertificatetemplate = parent
CA 签发证书CreateCertificateparent = CA 证书
生成 CSRCreateCertificateRequest提交给 CA
证书验证Verify验证信任链
证书池CertPool存储信任的 CA

证书生命周期

  1. 生成密钥 → 2. 创建 CSR → 3. CA 签发 → 4. 部署使用 → 5. 监控更新 → 6. 到期续期

参考资料


最后更新:2026-04-03
Go 版本:Go 1.23+
安全状态:✅ 推荐使用(正确配置下)