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)
}
安全最佳实践
✅ 推荐做法
-
使用强密钥
// ✅ ECDSA P-256 或更高 priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) // ✅ RSA 2048+ priv, err := rsa.GenerateKey(rand.Reader, 2048) -
使用安全的签名算法
// ✅ 推荐 SHA256WithRSA SHA384WithRSA SHA512WithRSA ECDSAWithSHA256 ECDSAWithSHA384 ECDSAWithSHA512 PureEd25519 -
设置合适的密钥用途
// 服务器证书 KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, // CA 证书 KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, IsCA: true, -
实现证书监控
// 检查证书有效期 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 } -
保护私钥
// ✅ 使用密码加密私钥 encryptedBlock, err := x509.EncryptPEMBlock( rand.Reader, "ENCRYPTED PRIVATE KEY", privBytes, password, x509.PEMCipherAES256, ) // ✅ 设置合适的文件权限 os.Chmod("private.key", 0600)
❌ 不安全做法
-
不要使用弱签名算法
// ❌ 避免 MD5WithRSA // 已攻破 SHA1WithRSA // 不推荐 ECDSAWithSHA1 // 不推荐 -
不要使用过短的密钥
// ❌ 避免 rsa.GenerateKey(rand.Reader, 1024) // 太短 -
不要硬编码私钥
// ❌ 绝对不要 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)
使用场景
| 场景 | 推荐方法 | 说明 |
|---|---|---|
| 解析证书 | ParseCertificate | PEM/DER 解码后解析 |
| 生成自签名证书 | CreateCertificate | template = parent |
| CA 签发证书 | CreateCertificate | parent = CA 证书 |
| 生成 CSR | CreateCertificateRequest | 提交给 CA |
| 证书验证 | Verify | 验证信任链 |
| 证书池 | CertPool | 存储信任的 CA |
证书生命周期
- 生成密钥 → 2. 创建 CSR → 3. CA 签发 → 4. 部署使用 → 5. 监控更新 → 6. 到期续期
参考资料
最后更新:2026-04-03
Go 版本:Go 1.23+
安全状态:✅ 推荐使用(正确配置下)