1. 为什么我们需要自签名SSL证书?
每次在浏览器里看到那个"不安全"的红色警告,作为开发者是不是觉得特别扎眼?特别是在本地开发环境测试HTTPS功能时,总不能每次都去买商业证书吧。这就是自签名SSL证书的用武之地了。
我最近在给内部系统配置Nginx时,就遇到了这个典型场景:测试环境需要HTTPS,但又不方便用Let's Encrypt(因为需要公网可访问)。自签名证书完美解决了这个问题,不过要让浏览器不报错,还需要一些额外配置。下面就把我的完整操作流程和踩坑经验分享给大家。
2. 证书生成全流程解析
2.1 准备工作:OpenSSL环境检查
首先确认系统已经安装了OpenSSL。在终端运行:
bash复制openssl version
如果显示版本信息(如OpenSSL 1.1.1),说明已经安装。如果没有,可以通过系统包管理器安装:
- Ubuntu/Debian:
sudo apt install openssl - CentOS/RHEL:
sudo yum install openssl
注意:不同版本的OpenSSL在参数上可能有细微差别,建议使用1.1.1及以上版本以获得更好的安全特性支持。
2.2 生成CA根证书(关键步骤)
自签名证书体系的核心是CA根证书。我们先创建一个专用目录来管理证书文件:
bash复制mkdir -p ~/ssl_certs && cd ~/ssl_certs
然后生成CA私钥(2048位RSA密钥):
bash复制openssl genrsa -out ca.key 2048
接着用这个私钥生成自签名的CA根证书(有效期10年):
bash复制openssl req -new -x509 -days 3650 -key ca.key -out ca.crt
执行后会交互式询问一些信息,可以按实际情况填写。其中Common Name(CN)建议使用有意义的名称,如"Local Development CA"。
2.3 生成服务器证书
现在我们来生成Nginx要使用的服务器证书。首先创建私钥:
bash复制openssl genrsa -out server.key 2048
然后创建证书签名请求(CSR):
bash复制openssl req -new -key server.key -out server.csr
这里特别注意:Common Name必须填写你访问网站时使用的域名。如果是本地测试,可以用"localhost"。
最后用CA证书签发服务器证书:
bash复制openssl x509 -req -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt
2.4 验证证书链
生成完成后,可以用以下命令验证证书:
bash复制openssl verify -CAfile ca.crt server.crt
如果显示"server.crt: OK",说明证书链是完整的。
3. Nginx配置HTTPS实战
3.1 基础SSL配置
将生成的server.key和server.crt复制到Nginx的配置目录(如/etc/nginx/ssl/),然后在Nginx配置中添加:
nginx复制server {
listen 443 ssl;
server_name localhost;
ssl_certificate /etc/nginx/ssl/server.crt;
ssl_certificate_key /etc/nginx/ssl/server.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
location / {
root /var/www/html;
index index.html;
}
}
3.2 性能优化配置
对于生产环境,建议添加以下优化参数:
nginx复制ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_prefer_server_ciphers on;
ssl_stapling on;
ssl_stapling_verify on;
3.3 HTTP重定向到HTTPS
通常我们会强制所有流量走HTTPS:
nginx复制server {
listen 80;
server_name localhost;
return 301 https://$host$request_uri;
}
4. 让浏览器信任自签名证书
4.1 导入CA根证书
这是最关键的一步。不同操作系统方法不同:
Windows:
- 双击ca.crt文件
- 选择"安装证书"
- 选择"本地计算机"
- 选择"将所有证书放入下列存储",浏览选择"受信任的根证书颁发机构"
macOS:
bash复制sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain ca.crt
Linux (Ubuntu):
bash复制sudo cp ca.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates
4.2 浏览器特定设置
Chrome/Edge:
- 访问chrome://flags/#allow-insecure-localhost
- 启用"Allow invalid certificates for resources loaded from localhost"
Firefox:
- 访问about:config
- 搜索security.enterprise_roots.enabled并设为true
4.3 证书信任问题排查
如果仍然看到警告,检查:
- 证书是否过期(
openssl x509 -in server.crt -noout -dates) - 证书的CN/SAN是否匹配访问的域名
- 是否清除了浏览器缓存
- 系统时间是否正确
5. 高级配置与维护
5.1 多域名支持(SAN证书)
现代浏览器要求证书包含Subject Alternative Name。生成CSR时需要创建配置文件san.cnf:
code复制[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req
[req_distinguished_name]
countryName = CN
stateOrProvinceName = Beijing
localityName = Beijing
organizationName = My Company
commonName = mysite.local
[v3_req]
subjectAltName = @alt_names
[alt_names]
DNS.1 = mysite.local
DNS.2 = *.mysite.local
然后生成CSR时指定这个配置:
bash复制openssl req -new -key server.key -out server.csr -config san.cnf
5.2 证书自动续期
虽然自签名证书可以设置很长有效期,但定期更换更安全。可以创建简单的续期脚本renew_ssl.sh:
bash复制#!/bin/bash
# 备份旧证书
cp server.crt server.crt.bak
cp server.key server.key.bak
# 生成新证书
openssl req -new -key server.key -out server.csr -config san.cnf
openssl x509 -req -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt
# 重载Nginx
nginx -t && nginx -s reload
5.3 安全性增强建议
-
私钥保护:
- 设置严格的文件权限:
chmod 600 server.key - 考虑使用密码保护私钥(但会增加Nginx启动时的交互)
- 设置严格的文件权限:
-
密钥轮换:
- 每6-12个月更换一次密钥对
- 生成新密钥:
openssl genrsa -out server_new.key 2048
-
HSTS配置:
在Nginx中添加头部强制HTTPS:nginx复制add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
6. 常见问题解决方案
6.1 Chrome报"NET::ERR_CERT_AUTHORITY_INVALID"
这通常是因为:
- CA证书没有正确导入到受信任的根证书存储
- 证书链不完整
- 证书的CN与访问的域名不匹配
解决方案:
- 确认CA证书已正确导入(见4.1节)
- 检查证书链:
openssl verify -CAfile ca.crt server.crt - 确保证书的CN和SAN包含你访问的域名
6.2 Safari报"此证书无效"
macOS上的Safari对证书要求特别严格:
- 确保证书有SAN扩展
- 密钥用法必须包含Digital Signature和Key Encipherment
- 有效期不能超过825天(macOS 10.15+限制)
可以通过以下命令创建兼容的证书:
bash复制openssl req -new -key server.key -out server.csr -config san.cnf
openssl x509 -req -days 825 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -extfile san.cnf -extensions v3_req
6.3 Nginx启动报"SSL_CTX_use_PrivateKey"错误
这通常是因为:
- 私钥文件损坏
- 私钥与证书不匹配
- 文件权限问题
检查步骤:
- 验证私钥:
openssl rsa -in server.key -check - 验证匹配性:
openssl x509 -noout -modulus -in server.crt | openssl md5应该与openssl rsa -noout -modulus -in server.key | openssl md5的输出一致 - 检查权限:
ls -l server.key应该显示只有所有者有读写权限
7. 生产环境建议
虽然自签名证书适合开发和测试环境,但生产环境建议:
- 使用Let's Encrypt免费证书
- 购买商业证书以获得更好的兼容性
- 考虑使用证书管理工具(如certbot)自动化续期
对于必须使用自签名证书的内部系统:
- 通过组策略(Windows)或MDM(macOS)批量部署CA证书
- 设置证书吊销列表(CRL)或OCSP响应
- 实现严格的证书生命周期管理
我在实际部署中发现,将CA证书和安装说明打包成内部文档,并配合定期检查脚本,可以大大减少证书相关问题的发生。比如这个简单的检查脚本check_ssl.sh:
bash复制#!/bin/bash
DOMAIN="yoursite.local"
EXPIRY=$(echo | openssl s_client -connect $DOMAIN:443 2>/dev/null | openssl x509 -noout -dates)
echo "证书有效期:"
echo "$EXPIRY"
echo -e "\n证书链验证:"
openssl s_client -connect $DOMAIN:443 -showcerts </dev/null 2>/dev/null | openssl verify -CAfile ca.crt