强曰为道

与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

第 6 章:OpenSSL 工具

第 6 章:OpenSSL 工具

OpenSSL 是 PKI 生态中最核心的工具集,几乎所有证书操作都可以通过它完成。本章系统讲解 OpenSSL 在证书管理中的常用命令和技巧。


6.1 OpenSSL 基础

版本与配置

# 查看版本
openssl version
# OpenSSL 3.0.2 15 Mar 2022 (Library: OpenSSL 3.0.2 15 Mar 2022)

# 查看完整编译信息
openssl version -a

# 查看默认目录
openssl version -d
# OPENSSLDIR: "/usr/lib/ssl"

# 查看支持的算法
openssl list -public-key-algorithms
openssl list -cipher-algorithms | head -20
openssl list -digest-algorithms | head -20

# 查看支持的椭圆曲线
openssl ecparam -list_curves | head -20

OpenSSL 3.x 变化

特性OpenSSL 1.1.xOpenSSL 3.x
Provider 架构内置模块化 Provider
弃用 API直接使用弃用警告
默认安全级别11(可配置)
OSSL_PARAM不支持新参数传递方式
FIPS 支持单独编译内置 FIPS Provider
# 查看 OpenSSL 3.x 的 Provider
openssl list -providers
# default
# base

6.2 生成密钥对

RSA 密钥

# 生成 RSA 2048 密钥
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out rsa-2048.key

# 生成 RSA 4096 密钥(更高安全性)
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:4096 -out rsa-4096.key

# 使用传统命令(仍然有效但推荐使用 genpkey)
openssl genrsa -out rsa-2048-legacy.key 2048

# 加密私钥(使用 AES-256-CBC)
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 \
  -aes-256-cbc -pass pass:MyPassword -out rsa-encrypted.key

# 查看私钥信息
openssl pkey -in rsa-2048.key -text -noout | head -10

ECDSA 密钥

# 生成 ECDSA P-256 密钥
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256 -out ecdsa-p256.key

# 生成 ECDSA P-384 密钥
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-384 -out ecdsa-p384.key

# 查看支持的曲线
openssl ecparam -list_curves | grep -E "prime256v1|secp384r1|secp521r1"

# 查看密钥信息
openssl pkey -in ecdsa-p256.key -text -noout

Ed25519 密钥

# 生成 Ed25519 密钥(需要 OpenSSL 1.1.1+)
openssl genpkey -algorithm Ed25519 -out ed25519.key

# 查看密钥
openssl pkey -in ed25519.key -text -noout

密钥大小对比

算法密钥长度安全等效文件大小性能
RSA2048 bit~112 bit~1.7 KB较慢
RSA4096 bit~140 bit~3.2 KB
ECDSA P-256256 bit~128 bit~230 B
ECDSA P-384384 bit~192 bit~320 B
Ed25519256 bit~128 bit~120 B最快

6.3 生成 CSR(Certificate Signing Request)

基本 CSR 生成

# 使用交互式提示
openssl req -new -key server.key -out server.csr

# 使用 -subj 参数(非交互式)
openssl req -new -key server.key -out server.csr \
  -subj "/C=CN/ST=Beijing/L=Beijing/O=MyOrganization/OU=IT/CN=example.com"

# 字段说明:
# C  - Country(国家代码,2 个字符)
# ST - State/Province(省/州)
# L  - Locality(城市)
# O  - Organization(组织名称)
# OU - Organizational Unit(部门)
# CN - Common Name(域名,通常是主域名)

包含 SAN 的 CSR

# 创建配置文件
cat > csr-san.cnf << 'EOF'
[req]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn
req_extensions = v3_req

[dn]
C = CN
ST = Beijing
L = Beijing
O = MyOrganization
OU = IT
CN = example.com

[v3_req]
subjectAltName = @alt_names
basicConstraints = CA:FALSE
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth

[alt_names]
DNS.1 = example.com
DNS.2 = www.example.com
DNS.3 = api.example.com
DNS.4 = *.staging.example.com
IP.1 = 192.168.1.100
IP.2 = 10.0.0.1
EOF

# 生成 CSR
openssl req -new -key server.key -out server-san.csr -config csr-san.cnf

# 验证 CSR
openssl req -in server-san.csr -text -noout | grep -A20 "Subject Alternative Name"

验证 CSR

# 查看 CSR 详细信息
openssl req -in server.csr -text -noout

# 验证 CSR 签名
openssl req -in server.csr -verify -noout

# 提取 CSR 中的公钥
openssl req -in server.csr -pubkey -noout > csr-pubkey.pem

# 对比密钥和 CSR 是否匹配
KEY_MD5=$(openssl pkey -in server.key -pubout 2>/dev/null | md5sum)
CSR_MD5=$(openssl req -in server.csr -pubkey -noout 2>/dev/null | md5sum)
if [ "$KEY_MD5" = "$CSR_MD5" ]; then
  echo "✅ 密钥和 CSR 匹配"
else
  echo "❌ 密钥和 CSR 不匹配!"
fi

6.4 自签名证书

基本自签名

# 一步生成自签名证书(RSA)
openssl req -x509 -newkey rsa:2048 -nodes \
  -keyout selfsigned.key -out selfsigned.crt \
  -days 365 \
  -subj "/C=CN/ST=Beijing/O=Dev/CN=localhost"

# ECDSA 自签名
openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:P-256 \
  -nodes -keyout selfsigned-ec.key -out selfsigned-ec.crt \
  -days 365 \
  -subj "/C=CN/ST=Beijing/O=Dev/CN=localhost"

生产级自签名配置

cat > selfsigned.cnf << 'EOF'
[req]
default_bits = 2048
prompt = no
default_md = sha256
x509_extensions = v3_ext
distinguished_name = dn

[dn]
C = CN
ST = Beijing
L = Beijing
O = MyOrganization
OU = DevOps
CN = myapp.internal

[v3_ext]
subjectAltName = @alt_names
basicConstraints = CA:FALSE
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth, clientAuth
authorityInfoAccess = @aia

[alt_names]
DNS.1 = myapp.internal
DNS.2 = *.myapp.internal
DNS.3 = localhost
IP.1 = 127.0.0.1
IP.2 = ::1

[aia]
OCSP;URI.0 = http://ocsp.myca.internal/
EOF

# 生成证书
openssl req -x509 -newkey rsa:2048 -nodes \
  -keyout myapp.key -out myapp.crt \
  -days 365 -config selfsigned.cnf

# 验证
openssl x509 -in myapp.crt -text -noout

从已有 CSR 生成自签名证书

# 从 CSR 签发自签名证书(相当于自己当 CA)
openssl x509 -req -in server.csr \
  -signkey server.key \
  -out server-selfsigned.crt \
  -days 365 \
  -extfile selfsigned.cnf -extensions v3_ext

6.5 创建私有 CA

创建 CA 目录结构

mkdir -p ~/my-ca/{certs,crl,newcerts,private}
chmod 700 ~/my-ca/private
touch ~/my-ca/index.txt
echo 1000 > ~/my-ca/serial

CA 配置文件

cat > ~/my-ca/ca.cnf << 'EOF'
[ca]
default_ca = CA_default

[CA_default]
dir               = /home/user/my-ca
certs             = $dir/certs
crl_dir           = $dir/crl
new_certs_dir     = $dir/newcerts
database          = $dir/index.txt
serial            = $dir/serial
RANDFILE          = $dir/private/.rand

private_key       = $dir/private/ca.key
certificate       = $dir/certs/ca.crt

crlnumber         = $dir/crlnumber
crl               = $dir/crl/ca.crl
crl_extensions    = crl_ext
default_crl_days  = 30

default_md        = sha256
name_opt          = ca_default
cert_opt          = ca_default
default_days      = 375
preserve          = no
policy            = policy_loose

[policy_loose]
countryName             = optional
stateOrProvinceName     = optional
organizationName        = optional
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

[req]
default_bits        = 2048
distinguished_name  = req_distinguished_name
string_mask         = utf8only
default_md          = sha256
x509_extensions     = v3_ca

[req_distinguished_name]
countryName                     = Country Name (2 letter code)
stateOrProvinceName             = State or Province Name
localityName                    = Locality Name
0.organizationName              = Organization Name
organizationalUnitName          = Organizational Unit Name
commonName                      = Common Name
emailAddress                    = Email Address

[v3_ca]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true
keyUsage = critical, digitalSignature, cRLSign, keyCertSign

[v3_intermediate_ca]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true, pathlen:0
keyUsage = critical, digitalSignature, cRLSign, keyCertSign

[server_cert]
basicConstraints = CA:FALSE
nsCertType = server
nsComment = "Generated by My CA"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer:always
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names

[alt_names]
DNS.1 = ${ENV::CERT_DOMAIN}
DNS.2 = www.${ENV::CERT_DOMAIN}

[crl_ext]
authorityKeyIdentifier = keyid:always
EOF

生成 CA 根证书

# 生成 CA 私钥
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:4096 \
  -out ~/my-ca/private/ca.key
chmod 400 ~/my-ca/private/ca.key

# 生成 CA 自签名证书
openssl req -config ~/my-ca/ca.cnf \
  -key ~/my-ca/private/ca.key \
  -new -x509 -days 7300 -sha256 \
  -extensions v3_ca \
  -out ~/my-ca/certs/ca.crt \
  -subj "/C=CN/ST=Beijing/O=MyCA/CN=My Root CA"

# 验证 CA 证书
openssl x509 -in ~/my-ca/certs/ca.crt -text -noout | head -20

用 CA 签发服务器证书

# 1. 生成服务器私钥
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 \
  -out server.key

# 2. 生成 CSR
export CERT_DOMAIN="example.com"
openssl req -config ~/my-ca/ca.cnf \
  -key server.key \
  -new -sha256 \
  -out server.csr \
  -subj "/C=CN/ST=Beijing/O=MyOrg/CN=example.com"

# 3. CA 签发证书
openssl ca -config ~/my-ca/ca.cnf \
  -extensions server_cert \
  -days 375 -notext -md sha256 \
  -in server.csr \
  -out server.crt \
  -batch

# 4. 验证证书
openssl verify -CAfile ~/my-ca/certs/ca.crt server.crt
# server.crt: OK

# 5. 查看证书
openssl x509 -in server.crt -text -noout | grep -E "Issuer|Subject|Not"

6.6 验证证书

基本验证

# 使用系统信任存储验证
openssl verify -CApath /etc/ssl/certs server.crt

# 使用指定 CA 验证
openssl verify -CAfile ca.crt server.crt

# 验证证书链
openssl verify -CAfile root-ca.crt -untrusted intermediate-ca.crt leaf.crt

# 验证远程服务器证书
echo | openssl s_client -connect example.com:443 -servername example.com \
  -verify 5 -CApath /etc/ssl/certs 2>&1 | grep "Verify"

检查证书详细信息

# 查看证书完整信息
openssl x509 -in cert.pem -text -noout

# 只查看特定字段
openssl x509 -in cert.pem -noout -subject         # 主题
openssl x509 -in cert.pem -noout -issuer           # 颁发者
openssl x509 -in cert.pem -noout -dates            # 有效期
openssl x509 -in cert.pem -noout -serial           # 序列号
openssl x509 -in cert.pem -noout -fingerprint      # 指纹
openssl x509 -in cert.pem -noout -ocsp_uri         # OCSP 地址
openssl x509 -in cert.pem -noout -text | grep -A5 "Subject Alternative"  # SAN

# 提取公钥
openssl x509 -in cert.pem -pubkey -noout

# 检查证书是否过期
openssl x509 -in cert.pem -checkend 0
# 如果已过期: Certificate will expire
# 如果未过期: Certificate will not expire

# 检查是否在 30 天内过期
openssl x509 -in cert.pem -checkend 2592000
# 2592000 = 30 * 24 * 3600 秒

对比两个证书

# 对比证书指纹
openssl x509 -in cert1.pem -noout -fingerprint -sha256
openssl x509 -in cert2.pem -noout -fingerprint -sha256

# 对比证书和私钥是否匹配
CERT_MD5=$(openssl x509 -in cert.pem -noout -modulus | md5sum)
KEY_MD5=$(openssl rsa -in key.pem -noout -modulus 2>/dev/null | md5sum)
[ "$CERT_MD5" = "$KEY_MD5" ] && echo "✅ 匹配" || echo "❌ 不匹配"

# 对于 ECDSA 密钥
CERT_PUB=$(openssl x509 -in cert.pem -pubkey -noout)
KEY_PUB=$(openssl pkey -in key.pem -pubout 2>/dev/null)
[ "$CERT_PUB" = "$KEY_PUB" ] && echo "✅ 匹配" || echo "❌ 不匹配"

6.7 格式转换

PEM ↔ DER

# PEM → DER
openssl x509 -in cert.pem -outform DER -out cert.der

# DER → PEM
openssl x509 -in cert.der -inform DER -outform PEM -out cert.pem

# 私钥 PEM → DER
openssl pkey -in key.pem -outform DER -out key.der

# 私钥 DER → PEM
openssl pkey -in key.der -inform DER -outform PEM -out key.pem

创建 PKCS#12 (.p12)

# 将证书和私钥打包为 PKCS#12
openssl pkcs12 -export \
  -in cert.pem -inkey key.pem \
  -certfile chain.pem \
  -out cert.p12 \
  -name "my-server" \
  -passout pass:changeit

# 查看 PKCS#12 内容
openssl pkcs12 -in cert.p12 -info -nokeys -passin pass:changeit
openssl pkcs12 -in cert.p12 -info -nocerts -passin pass:changeit

# 从 PKCS#12 提取证书
openssl pkcs12 -in cert.p12 -clcerts -nokeys -out cert-extracted.pem -passin pass:changeit

# 从 PKCS#12 提取私钥
openssl pkcs12 -in cert.p12 -nocerts -out key-extracted.pem -passin pass:changeit

# 去除私钥密码保护
openssl pkey -in key-encrypted.pem -out key-decrypted.pem -passin pass:changeit

PKCS#12 → Java Keystore

# 将 PKCS#12 导入 Java Keystore
keytool -importkeystore \
  -srckeystore cert.p12 -srcstoretype PKCS12 -srcstorepass changeit \
  -destkeystore keystore.jks -deststoretype JKS -deststorepass changeit \
  -alias my-server

# 查看 Java Keystore
keytool -list -keystore keystore.jks -storepass changeit

# 导入 CA 证书到 Java Keystore
keytool -import -trustcacerts \
  -alias my-ca \
  -file ca.crt \
  -keystore $JAVA_HOME/lib/security/cacerts \
  -storepass changeit

6.8 密钥管理

加密/解密私钥

# 加密私钥
openssl pkey -in key.pem -aes-256-cbc -passout pass:MyPassword -out key-encrypted.pem

# 解密私钥
openssl pkey -in key-encrypted.pem -passin pass:MyPassword -out key-decrypted.pem

# 修改私钥密码
openssl pkey -in key-encrypted.pem -passin pass:OldPassword \
  -aes-256-cbc -passout pass:NewPassword -out key-re-encrypted.pem

密钥安全存储

# 设置严格的文件权限
chmod 600 *.key
chmod 644 *.crt *.pem

# 建议的目录权限
chmod 700 /etc/ssl/private/

# 验证私钥不被泄露
ls -la /etc/ssl/private/
# drwx------ 2 root root 4096 ... .
# -rw------- 1 root root 1704 ... server.key

🔒 安全:私钥文件权限必须是 600(仅所有者可读写)。如果私钥被泄露,所有使用该证书的服务都应视为已泄露,需要立即吊销证书并重新签发。


6.9 实用脚本

证书信息一键查看

#!/usr/bin/env bash
# cert-info.sh - 快速查看证书信息
# 用法: ./cert-info.sh <file_or_url>

TARGET="${1:?用法: $0 <file_or_url>}"

if [ -f "$TARGET" ]; then
  # 本地文件
  openssl x509 -in "$TARGET" -text -noout
elif echo "$TARGET" | grep -qE "^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"; then
  # 域名
  echo | openssl s_client -connect "${TARGET}:443" -servername "$TARGET" \
    -verify 5 -CApath /etc/ssl/certs 2>/dev/null | openssl x509 -text -noout
else
  echo "❌ 无效参数: $TARGET"
  exit 1
fi

密钥与证书匹配检查

#!/usr/bin/env bash
# check-match.sh - 检查密钥、证书、CSR 是否匹配
# 用法: ./check-match.sh <key> <cert_or_csr>

KEY="${1:?用法: $0 <key> <cert_or_csr>}"
CERT="${2:?用法: $0 <key> <cert_or_csr>}"

# 获取密钥模数
KEY_MOD=$(openssl pkey -in "$KEY" -pubout 2>/dev/null | openssl md5)

# 获取证书/CSR 模数
if head -1 "$CERT" | grep -q "CERTIFICATE"; then
  CERT_MOD=$(openssl x509 -in "$CERT" -pubkey -noout 2>/dev/null | openssl md5)
elif head -1 "$CERT" | grep -q "CERTIFICATE REQUEST"; then
  CERT_MOD=$(openssl req -in "$CERT" -pubkey -noout 2>/dev/null | openssl md5)
else
  echo "❌ 无法识别文件格式"
  exit 1
fi

if [ "$KEY_MOD" = "$CERT_MOD" ]; then
  echo "✅ 密钥和证书/CSR 匹配"
else
  echo "❌ 密钥和证书/CSR 不匹配"
  echo "  Key MD5:  $KEY_MOD"
  echo "  Cert MD5: $CERT_MOD"
fi

6.10 本章小结

命令类别常用命令说明
密钥生成openssl genpkey推荐的密钥生成方式
CSR 生成openssl req -new配合 -config 使用 SAN
自签名openssl req -x509开发测试用
CA 签发openssl ca需要 CA 配置文件
验证openssl verify验证证书链
查看openssl x509 -text查看证书详情
转换openssl x509/pkey/pkcs12PEM/DER/PKCS#12 互转

📚 扩展阅读


上一章第 5 章:证书管理 下一章第 7 章:Let’s Encrypt — 学习 ACME 协议和 Let’s Encrypt 自动化证书签发。