技巧杂烩

OpenSSL 自签证书

使用 OpenSSL 生成和签发自签名的根证书、服务端证书和客户端证书,步骤涵盖密钥生成、证书请求创建和证书签发的完整流程。

OpenSSL

OpenSSL 自签证书

环境准备

请确认你的操作系统已安装 OpenSSL(推荐 3.x 版本):

openssl version

如果版本低于 1.1.1,建议升级至最新版本以获得对 TLS 1.3 和更强加密算法的支持。


1. 生成 Root CA(根证书)

根证书是整个信任链的基础。我们使用椭圆曲线(ECC)算法 secp384r1,相比传统 RSA 密钥更短、性能更好、安全性更高。

1.1 生成 CA 私钥

openssl ecparam -name secp384r1 -genkey -noout -out rootCA.key
参数说明
ecparam椭圆曲线密钥参数命令
-name secp384r1选择 NIST P-384 椭圆曲线(推荐,安全性等效 RSA 7680 位)
-genkey生成密钥
-noout不输出参数到标准输出
-out rootCA.key私钥输出文件

设置私钥文件权限,确保安全:

chmod 400 rootCA.key

1.2 生成自签名 CA 证书

openssl req -x509 -new -nodes -key rootCA.key -sha384 -days 3650 \
  -out rootCA.crt \
  -subj "/C=CN/O=MyOrg/CN=MyECCRootCA"
参数说明
-x509输出自签名 X.509 证书(而非 CSR)
-sha384使用 SHA-384 摘要算法(配合 ECC P-384 推荐)
-days 3650有效期 10 年
-subj证书主题,可根据实际情况自定义

💡 产出文件:rootCA.key(CA 私钥,严格保密)、rootCA.crt(CA 公钥证书,可分发)


2. 生成服务端证书

2.1 生成服务端私钥

openssl ecparam -name secp384r1 -genkey -noout -out server.key
chmod 400 server.key

2.2 创建 OpenSSL 配置文件(推荐方式)

使用配置文件可以更清晰地管理 SAN(Subject Alternative Name)等扩展字段,避免命令行过长且容易出错。

创建 server.cnf

[req]
prompt = no
distinguished_name = dn
req_extensions = v3_req

[dn]
C  = CN
O  = MyOrg
CN = www.example.com

[v3_req]
subjectAltName = @alt_names
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth

[alt_names]
DNS.1 = www.example.com
DNS.2 = example.com
IP.1  = 127.0.0.1

⚠️ subjectAltName(SAN)是现代浏览器和客户端验证证书的关键字段,仅设置 CN 已不被信任。务必在 SAN 中列出所有需要的域名和 IP。

2.3 生成服务端证书请求(CSR)

openssl req -new -key server.key -out server.csr -config server.cnf

2.4 使用 CA 签发服务端证书

创建签发用扩展文件 server_ext.cnf

subjectAltName = @alt_names
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth

[alt_names]
DNS.1 = www.example.com
DNS.2 = example.com
IP.1  = 127.0.0.1

签发证书:

openssl x509 -req -in server.csr \
  -CA rootCA.crt -CAkey rootCA.key -CAcreateserial \
  -out server.crt -days 825 -sha384 \
  -extfile server_ext.cnf

📌 Apple 等平台要求 TLS 服务端证书有效期不超过 825 天,建议遵循此限制以保证最大兼容性。


3. 生成客户端证书(mTLS 双向认证)

如果你需要双向 TLS(mTLS),还需为客户端生成证书。

3.1 生成客户端私钥

openssl ecparam -name secp384r1 -genkey -noout -out client.key
chmod 400 client.key

3.2 创建客户端配置文件

创建 client.cnf

[req]
prompt = no
distinguished_name = dn
req_extensions = v3_req

[dn]
C  = CN
O  = MyOrg
CN = client.local

[v3_req]
subjectAltName = DNS:client.local
keyUsage = digitalSignature
extendedKeyUsage = clientAuth

3.3 生成客户端 CSR 并签发

openssl req -new -key client.key -out client.csr -config client.cnf

创建签发用扩展文件 client_ext.cnf

subjectAltName = DNS:client.local
keyUsage = digitalSignature
extendedKeyUsage = clientAuth

签发客户端证书:

openssl x509 -req -in client.csr \
  -CA rootCA.crt -CAkey rootCA.key -CAcreateserial \
  -out client.crt -days 825 -sha384 \
  -extfile client_ext.cnf

4. 验证证书

查看证书详细信息:

openssl x509 -in server.crt -text -noout

验证证书链是否正确:

openssl verify -CAfile rootCA.crt server.crt
openssl verify -CAfile rootCA.crt client.crt

5. 证书文件说明

文件名说明是否保密
rootCA.keyCA 私钥严格保密
rootCA.crtCA 公钥证书可公开分发
server.key服务端私钥保密
server.csr服务端证书请求签发后可删除
server.crt服务端证书可公开
client.key客户端私钥保密
client.csr客户端证书请求签发后可删除
client.crt客户端证书可公开

6. Nginx 配置示例

6.1 单向 TLS(标准 HTTPS)

将证书文件复制到 Nginx 可访问的目录:

sudo mkdir -p /etc/nginx/ssl
sudo cp server.crt server.key rootCA.crt /etc/nginx/ssl/
sudo chmod 600 /etc/nginx/ssl/server.key

Nginx 配置(/etc/nginx/conf.d/example.conf):

server {
    listen 80;
    server_name www.example.com example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name www.example.com example.com;

    # 证书与私钥
    ssl_certificate     /etc/nginx/ssl/server.crt;
    ssl_certificate_key /etc/nginx/ssl/server.key;

    # TLS 协议版本(仅启用 TLS 1.2 和 1.3)
    ssl_protocols TLSv1.2 TLSv1.3;

    # 加密套件(优先使用服务端配置)
    ssl_prefer_server_ciphers on;
    ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305';

    # ECDH 曲线
    ssl_ecdh_curve secp384r1:X25519;

    # SSL 会话缓存
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
    ssl_session_tickets off;

    # HSTS(HTTP 严格传输安全)
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

    # OCSP Stapling(自签证书可跳过此项)
    # ssl_stapling on;
    # ssl_stapling_verify on;
    # ssl_trusted_certificate /etc/nginx/ssl/rootCA.crt;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

6.2 双向 TLS(mTLS)

在上述配置基础上,添加客户端证书验证:

server {
    listen 443 ssl;
    server_name www.example.com;

    ssl_certificate     /etc/nginx/ssl/server.crt;
    ssl_certificate_key /etc/nginx/ssl/server.key;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305';

    # 客户端证书验证
    ssl_client_certificate /etc/nginx/ssl/rootCA.crt;
    ssl_verify_client on;
    ssl_verify_depth 2;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-SSL-Client-CN $ssl_client_s_dn_cn;
        proxy_set_header X-SSL-Client-Verify $ssl_client_verify;
    }
}

6.3 测试配置并重载

# 检查 Nginx 配置语法
sudo nginx -t

# 重载配置
sudo nginx -s reload

使用 curl 验证连接:

# 单向 TLS(需信任自签 CA)
curl --cacert rootCA.crt https://www.example.com

# 双向 TLS(附带客户端证书)
curl --cacert rootCA.crt --cert client.crt --key client.key https://www.example.com

文章标题:OpenSSL 自签证书

文章作者:浅小沫

文章链接:https://blog.truimo.com/posts/openssl-self-signed-cert


您可以自由在任何媒介以任何形式分享本作品,但需署名,且不得用于商业目的或改编。若分发衍生作品,须采用相同的许可协议。

本博客的所有原创内容采用 CC BY-NC-ND 4.0 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可。