给测试环境、内网系统或还没排上 CA 的域名配 HTTPS,最顺手的招是 openssl 自签证书:一条命令、两个文件。但"生成成功"不等于"浏览器不报错"------Safari 可能提示"证书名称与输入不匹配",Chrome 可能说证书无效。这篇一次讲清生成与"为什么生成完还不生效"。

四步固定顺序:生成、自查、试用、排错;跳步的代价是拿着本地文件猜半天。
一、先定边界:快速生成的是自签证书
公开受信任的证书只能由 CA 签发,浏览器只信任内置根证书库。自签证书密码学上完全可用,但不在任何浏览器的信任列表里------公开站点访客必见警告,这就是它的边界。适用范围:测试环境、内网服务、客户端会手动导入信任的场景。要"零警告",只有 CA 签发一条路。
二、一条命令:生成含 SAN 的自签域名证书
OpenSSL 1.1.1 及之后可直接用 -addext 写入 SAN:
bash
openssl req -x509 -newkey rsa:2048 -nodes \
-keyout example.com.key -out example.com.crt \
-days 365 \
-subj "/CN=example.com" \
-addext "subjectAltName=DNS:example.com,DNS:www.example.com,IP:192.0.2.10"
-x509 输出自签证书而非 CSR;-newkey 现场生成密钥对;-nodes 私钥不加密(记得 chmod 600);-days 是有效期;SAN 写在 -addext 里。
执行后得到 .key 与 .crt;证书的 SAN 列全了三个名字(两枚 DNS 加一个 IP),这就是后续校验的依据。
三、生成完先自查:名称、期限、私钥配对
别急着部署,先核对材料本身:
bash
# 看主题、有效期和 SAN
openssl x509 -in example.com.crt -noout -subject -dates -ext subjectAltName
# 配对校验:两个指纹必须一致
openssl x509 -in example.com.crt -noout -pubkey | openssl sha256
openssl pkey -in example.com.key -pubout | openssl sha256
第一条输出三块:subject、dates、SAN------对照要访问的主机名,缺谁补谁。第二条是常被漏掉的配对校验:两行 sha256 一致,说明证书与私钥是同一对。
四、为什么只写 CN 会"名称不匹配"
CN 曾是浏览器校验域名的唯一字段,现在反过来了:RFC 9525 规定服务器身份只能写在 subjectAltName 里,CN-ID 已失效------现代浏览器执行的就是这条规则。
于是有最常见的坑:-subj 没问题、少了 -addext,证书没有 SAN------名称校验直接失败。Safari 证书面板给出"证书名称与输入不匹配"(Certificate name does not match input),Chrome 报 NET::ERR_CERT_COMMON_NAME_INVALID。
两个细节:CN 不能带协议前缀(https://example.com 是错的),也不建议写 IP。
五、多域名、通配符、IP:SAN 的正确写法
SAN 是逗号分隔清单,每项带类型前缀 DNS: 或 IP::
bash
# 多域名 + 通配符 + IP 的完整写法
-addext "subjectAltName=DNS:example.com,DNS:www.example.com,DNS:*.example.com,IP:192.0.2.10"
三条规则:通配符只能占最左侧完整标签------*.example.com 不匹配 a.b.example.com,也不匹配裸的 example.com,根域要单列;用 IP 访问就把 IP 以 IP: 前缀写进 SAN;SAN 只写主机名,不带端口、路径、协议头。
六、旧版 OpenSSL:配置文件法同样一条命令
CentOS 7 自带的 OpenSSL 1.0.2 没有 -addext,改用配置文件:
ini
# san.cnf
[req]
distinguished_name = dn
x509_extensions = v3_req
prompt = no
[dn]
CN = example.com
[v3_req]
subjectAltName = @alt
[alt]
DNS.1 = example.com
DNS.2 = www.example.com
bash
openssl req -x509 -newkey rsa:2048 -nodes -days 365 \
-keyout example.com.key -out example.com.crt \
-config san.cnf
结果与命令行法等效。注意别漏写 x509_extensions 那行,否则 SAN 会悄悄丢失;生成后照旧自查。
七、放到服务里试用:读回"实际发出的证书"
材料无误后挂到服务上试,以 Nginx 为例:
nginx
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/nginx/certs/example.com.crt;
ssl_certificate_key /etc/nginx/certs/example.com.key;
}
nginx -t 通过再 reload,然后读回实际发出的证书:
bash
openssl s_client -connect 127.0.0.1:443 -servername example.com /dev/null \
| openssl x509 -noout -subject -ext subjectAltName
排查名称不匹配,第一步永远是先读回"实发证书",再和本地对照。没有 Nginx 也能自测:s_server 挂证书、s_client 连回环。
八、生成成功但浏览器仍报错:典型场景对照
| 现象 | 常见原因 | 快速验证 | 处理方向 |
|---|---|---|---|
| 证书名称与输入不匹配 / ERR_CERT_COMMON_NAME_INVALID | SAN 缺失或不含访问主机名 | x509 -ext subjectAltName 对照地址栏 | 补齐 SAN 后重签 |
| 证书不受信任 / 未知颁发者 | 自签证书不在信任库(预期) | 看颁发者是否为自己 | 导入信任或换 CA 证书 |
| 同一 IP 多站点返回别人的证书 | 未发送 SNI,默认站点抢先应答 | s_client 带 -servername 对比 | 修 server_name 与默认站点 |
| 用 IP 访问报名称错误 | SAN 无 IP 条目 | 看 SAN 是否含 IP: | 补 IP: 条目重签或改用域名 |
"证书名称与输入不匹配"是 Safari 对名称校验失败的说明:看 SAN、对照地址栏、缺谁补谁。第二行是自签的预期行为,不是配置错误。最后一行常被误判成"装错证书":同 IP 多站点未发 SNI 时默认站点抢先应答。
九、验收清单
- openssl version ≥ 1.1.1 或配置文件法等效;
- 生成命令含 SAN,覆盖根域、www、通配、IP;
- x509 -ext subjectAltName 与访问地址逐一对上;
- 证书与私钥公钥指纹一致;
- nginx -t 通过再 reload,s_client 读回实发证书;
- 名称校验不报错;"不受信任"视为自签预期;
- 私钥权限 600,不进 git。
自签解决加密和名称,解决不了信任。把 SAN 写全、把实发证书读回,是成本最低的两个动作。
官方参考:OpenSSL req 文档(-addext);RFC 9525 服务身份与 SAN;ssl.com:Common Name 与 SAN。