1. 为什么需要自签HTTPS证书?
在Web服务部署中,HTTPS早已成为标配而非可选。但很多开发者在本地测试、内网服务或非生产环境部署时,往往会遇到一个现实问题:如何快速获得可用的HTTPS证书?商业证书需要付费申请,Let's Encrypt等免费方案又需要域名验证。这时,自签名证书(Self-Signed Certificate)就成了最实用的解决方案。
我最近在为一个金融类项目搭建测试环境时,就遇到了这个典型场景。客户要求所有接口必须走HTTPS,但测试服务器用的是内网IP,无法通过常规CA验证。通过OpenSSL自签证书配合Nginx配置,半小时就解决了问题。这种方案特别适合:
- 本地开发环境调试
- 企业内部系统
- 临时演示环境
- 微服务间通信加密
注意:自签名证书浏览器会显示"不安全"警告,这是正常现象。生产环境请务必使用可信CA颁发的证书。
2. OpenSSL工具链准备
2.1 安装验证OpenSSL
大多数Linux系统已预装OpenSSL,通过以下命令检查版本:
openssl version # 期望输出类似:OpenSSL 1.1.1f 31 Mar 2020如果提示命令不存在,则需要手动安装:
- Ubuntu/Debian:
sudo apt update && sudo apt install openssl - CentOS/RHEL:
sudo yum install openssl - macOS(自带但可能版本旧):
brew install openssl
2.2 关键文件说明
执行证书生成操作前,需要了解几个核心文件:
server.key- 私钥文件,必须严格保密server.csr- 证书签名请求文件server.crt- 最终生成的证书文件server.pem- 有时用于合并证书链
我习惯在/etc/nginx/ssl目录下管理这些文件:
sudo mkdir -p /etc/nginx/ssl && cd /etc/nginx/ssl sudo chmod 700 /etc/nginx/ssl # 限制目录权限3. 证书生成全流程
3.1 生成RSA私钥
首先生成2048位的私钥(安全与性能的平衡点):
openssl genrsa -out server.key 2048 # 如果想增加私钥安全性,可以添加-aes256加密: # openssl genrsa -aes256 -out server.key 2048关键参数解析:
genrsa:生成RSA密钥-out:指定输出文件2048:密钥长度(1024已不安全,4096性能开销大)
实测建议:测试环境用2048位足够,金融等高安全场景建议4096位。加密的私钥每次Nginx重启都需要输入密码,自动化部署时需权衡。
3.2 创建CSR文件
CSR(Certificate Signing Request)包含申请者的公钥和身份信息:
openssl req -new -key server.key -out server.csr执行后会交互式询问信息,典型输入示例:
Country Name (2 letter code) [AU]:CN State or Province Name (full name) [Some-State]:Beijing Locality Name (eg, city) []:Beijing Organization Name (eg, company) [Internet Widgits Pty Ltd]:MyDev Inc Organizational Unit Name (eg, section) []:DevOps Common Name (e.g. server FQDN or YOUR name) []:test.example.com Email Address []:admin@example.com Please enter the following 'extra' attributes A challenge password []: # 直接回车留空 An optional company name []: # 直接回车留空重点说明:
Common Name必须与最终访问的域名一致(本地测试可用IP)- 其他字段按需填写,但不可全部为空
- 生产环境建议所有字段规范填写
3.3 生成自签名证书
关键命令(有效期设为365天):
openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt高级用法:生成SAN证书(支持多域名/IP)需要额外配置文件san.cnf:
[req] distinguished_name = req_distinguished_name req_extensions = v3_req prompt = no [req_distinguished_name] C = CN ST = Beijing L = Beijing O = MyDev Inc OU = DevOps CN = test.example.com [v3_req] keyUsage = keyEncipherment, dataEncipherment extendedKeyUsage = serverAuth subjectAltName = @alt_names [alt_names] DNS.1 = test.example.com DNS.2 = dev.example.com IP.1 = 192.168.1.100 IP.2 = 127.0.0.1生成命令变为:
openssl req -new -key server.key -out server.csr -config san.cnf openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt -extensions v3_req -extfile san.cnf4. Nginx配置实战
4.1 基础HTTPS配置
在Nginx配置文件中添加:
server { listen 443 ssl; server_name test.example.com; ssl_certificate /etc/nginx/ssl/server.crt; ssl_certificate_key /etc/nginx/ssl/server.key; ssl_session_cache shared:SSL:1m; ssl_session_timeout 5m; location / { root /var/www/html; index index.html; } }4.2 增强安全配置
推荐的安全强化配置:
ssl_protocols TLSv1.2 TLSv1.3; # 禁用老旧协议 ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256'; ssl_prefer_server_ciphers on; ssl_stapling off; # 自签名证书无法开启OCSP # 启用HSTS(生产环境谨慎使用) add_header Strict-Transport-Security "max-age=63072000" always;4.3 HTTP强制跳转HTTPS
在80端口配置中添加:
server { listen 80; server_name test.example.com; return 301 https://$host$request_uri; }5. 浏览器信任证书
5.1 客户端安装证书
虽然自签名证书不受公共CA信任,但可以手动添加到系统信任库:
Windows:
- 双击
.crt文件 - 选择"安装证书"
- 存储位置选"本地计算机"
- 选择"将所有证书放入下列存储",浏览选择"受信任的根证书颁发机构"
- 双击
macOS:
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain server.crtLinux(Chrome):
# 对于Debian系 sudo cp server.crt /usr/local/share/ca-certificates/ sudo update-ca-certificates
5.2 解决Chrome的NET::ERR_CERT_AUTHORITY_INVALID
即使安装了证书,Chrome可能仍显示警告。需要:
- 访问
chrome://flags/#allow-insecure-localhost - 启用"Allow invalid certificates for resources loaded from localhost"
6. 常见问题排查
6.1 证书验证失败
使用OpenSSL验证证书链:
openssl verify -CAfile server.crt server.crt典型错误及解决方案:
unable to get local issuer certificate→ 证书链不完整certificate has expired→ 重新生成证书hostname mismatch→ 检查CN和SAN配置
6.2 Nginx报错排查
查看Nginx错误日志:
tail -f /var/log/nginx/error.log常见错误:
SSL_CTX_use_PrivateKey_file: key values mismatch→ 证书密钥不匹配no "ssl_certificate" is defined→ 路径配置错误SSL routines:ssl_choose_client_version:unsupported protocol→ 协议配置问题
7. 进阶技巧与优化
7.1 证书自动续期脚本
创建renew_ssl.sh:
#!/bin/bash cd /etc/nginx/ssl openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ -keyout server.key -out server.crt \ -subj "/C=CN/ST=Beijing/L=Beijing/O=MyDev Inc/OU=IT/CN=test.example.com" nginx -s reload添加到crontab(每月1号凌晨3点执行):
0 3 1 * * /etc/nginx/ssl/renew_ssl.sh7.2 性能优化参数
在高并发场景下调整:
ssl_buffer_size 4k; # 减少初始加密数据包大小 ssl_session_tickets on; # 启用会话票证 ssl_early_data on; # 启用0-RTT(TLS 1.3)7.3 多域名配置方案
单个Nginx实例支持多个证书:
server { listen 443 ssl; server_name api.example.com; ssl_certificate /path/to/api.crt; ssl_certificate_key /path/to/api.key; # ... } server { listen 443 ssl; server_name app.example.com; ssl_certificate /path/to/app.crt; ssl_certificate_key /path/to/app.key; # ... }8. 安全注意事项
私钥保护:
- 设置严格的文件权限:
chmod 400 server.key - 禁止私钥上传到代码仓库
- 考虑使用HSM(硬件安全模块)存储密钥
- 设置严格的文件权限:
证书生命周期管理:
- 记录每个证书的到期时间
- 建立续期提醒机制
- 废弃证书及时吊销(虽然自签名无法全局吊销)
加密算法选择:
- 优先选择ECDSA over RSA
- 使用SHA-256及以上哈希算法
- 禁用SSLv3、TLS 1.0/1.1
内网特别建议:
- 即使在内网也全链路HTTPS
- 为所有内部服务分配唯一域名
- 建立内部CA统一管理证书
在实际操作中,我发现很多团队容易忽视证书管理,导致临时抱佛脚。建议将证书生成流程脚本化,纳入DevOps自动化流程。对于需要团队共享的环境,可以使用密码保护的PKCS#12格式打包证书:
openssl pkcs12 -export -out bundle.p12 -inkey server.key -in server.crt