在开发和测试环境中,我们经常需要为内部网站生成自签名 SSL 证书。本文将详细介绍如何生成包含 IP 地址和域名的 SSL 证书,并配置浏览器信任该证书。
脚本解析
以下是一个完整的自签名 SSL 证书生成脚本:
#!/bin/bash
# 进入证书目录
cd /root/certs
# 定义 IP 地址(替换为您的实际 IP)
IP_ADDRESS="36.xx.xx.xx"
# 创建证书配置文件,包含 IP 地址
cat > server_ext.cnf <<EOF
[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req
prompt = no
[req_distinguished_name]
C = CN
ST = Beijing
L = Beijing
O = YourCompany
CN = www.sample.com
[v3_req]
basicConstraints = CA:FALSE
keyUsage = digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = www.sample.com
DNS.2 = sample.com
IP.1 = ${IP_ADDRESS}
EOF
# 重新生成私钥
openssl genrsa -out server.key 2048
# 生成证书签名请求
openssl req -new -key server.key -out server.csr -config server_ext.cnf
# 生成证书(包含扩展)
openssl x509 -req -days 3650 -in server.csr \
-signkey server.key -out server.crt \
-extensions v3_req -extfile server_ext.cnf
# 检查证书扩展
echo "=== 检查证书扩展 ==="
openssl x509 -in server.crt -text -noout | grep -A 10 "X509v3 Subject Alternative Name"
# 重启 Nginx
# systemctl restart nginx
echo "证书已重新生成,包含 IP 地址: ${IP_ADDRESS}"
echo "请重新导入 server.crt 到 Chrome 的受信任根证书颁发机构"
脚本关键部分解析
1. 证书配置文件 (server_ext.cnf)
配置文件包含以下几个关键部分:
-
[req] 部分: 定义证书请求的基本参数
distinguished_name: 指定 DN(可分辨名称)配置节req_extensions: 指定要使用的扩展节(v3_req)prompt = no: 禁用交互式提示,使用预设值
-
[req_distinguished_name] 部分: 定义证书主题信息
C: 国家(Country)ST: 州/省(State/Province)L: 城市(Locality)O: 组织(Organization)CN: 通用名称(Common Name)- 主要域名
-
[v3_req] 部分: 证书扩展配置
basicConstraints = CA:FALSE: 表示这不是 CA 证书keyUsage: 密钥用法,这里指定数字签名和密钥加密extendedKeyUsage: 扩展密钥用法,指定为服务器身份验证subjectAltName = @alt_names: 引用 alt_names 节中的 SAN 值
-
[alt_names] 部分: SAN(主题备用名称)定义
DNS.1,DNS.2: DNS 名称条目IP.1: IP 地址条目,允许通过 IP 访问
2. OpenSSL 命令解析
-
生成私钥:
openssl genrsa -out server.key 2048- 生成 2048 位的 RSA 私钥
-
生成 CSR:
openssl req -new -key server.key -out server.csr -config server_ext.cnf- 使用私钥和配置文件生成证书签名请求
-
生成证书:
openssl x509 -req -days 3650 ...- 使用 CSR 和私钥生成有效期为 10 年的自签名证书
- 应用 v3_req 扩展中的配置
证书基本结构解析
证书的层次结构
- 版本号: X.509 v3(当前标准)
- 序列号: 证书的唯一标识符
- 签名算法: 用于签名证书的算法(如 sha256WithRSAEncryption)
- 颁发者: 颁发证书的实体(自签名时与主题相同)
- 有效期: 证书的有效起止时间
- 主题: 证书持有者的信息
- 公钥信息: 持有者的公钥和算法
- 扩展域: 包含各种扩展信息(最重要的部分)
关键扩展字段
1. 基本约束 (basicConstraints)
CA:FALSE表示这是终端实体证书,不能用作 CA 证书CA:TRUE表示这是 CA 证书,可以签发其他证书
2. 密钥用法 (keyUsage)
定义证书中公钥的用途:
- digitalSignature: 允许进行数字签名操作(TLS 握手必需)
- keyEncipherment: 允许密钥交换时加密密钥
3. 扩展密钥用法 (extendedKeyUsage)
进一步限制证书用途:
- serverAuth: TLS Web 服务器身份验证
- clientAuth: TLS Web 客户端身份验证
- codeSigning: 代码签名
4. 主题备用名称 (subjectAltName)
现代 TLS 实现中最重要的扩展之一:
- DNS 名称:
DNS:example.com - IP 地址:
IP:192.168.1.1 - 电子邮件:
email:user@example.com
为什么需要 SAN?
- 兼容性: 现代浏览器(Chrome 58+)已不检查 CN 字段,只检查 SAN
- 灵活性: 一个证书可以保护多个域名和 IP 地址
- 安全性: 防止证书误用,明确指定有效名称
浏览器导入证书指南
打开Chrome浏览器:
- 打开设置-> 隐私和安全
- 选择安全-> 管理证书
- 选择"管理Windows导入的证书",点击"受信任的证书颁发机构",导入
server.crt文件
- 重启Chrome
验证导入结果
- 重启 Chrome,访问
chrome://settings/certificates - 选择受信任的根证书颁发机构选项卡
- 查找颁发者为
www.sample.com的证书
重启并测试
- 完全关闭所有 Chrome 窗口
- 重新打开 Chrome
- 访问以下地址测试:
https://www.sample.com:8443https://xx.xx.xx.xx:8443
https证书将不报错:
Nginx 配置示例
server {
listen 8443 ssl;
listen [::]:8443 ssl;
server_name www.sample.com 36.xx.xx.xx;
root /usr/share/nginx/html;
# SSL 证书配置
ssl_certificate /root/certs/server.crt;
ssl_certificate_key /root/certs/server.key;
# SSL 协议配置
ssl_protocols TLSv1.2 TLSv1.3;
# 默认文件
index index.html index.htm;
location / {
try_files $uri $uri/ =404;
}
}
常见问题与解决方案
1. 证书仍然显示不安全
可能原因:
- 证书未正确导入到受信任的根证书颁发机构
- 证书缺少必要的扩展
- 浏览器缓存了旧的证书信息,未重启Chrome
解决方案:
- 重新导入证书并确保选择正确的存储位置
- 检查证书是否包含
digitalSignature密钥用法 - 清除浏览器 SSL 状态缓存
通过遵循本指南,您可以创建一个既支持域名又支持 IP 地址访问的 HTTPS 环境,消除浏览器安全警告,提高开发和测试效率。