服务器和代理
key values mismatch:Nginx 证书和私钥不匹配
nginx: [emerg] SSL_CTX_use_PrivateKey("/etc/nginx/ssl/example.com.key") failed (SSL: error:05800074:x509 certificate routines::key values mismatch)key values mismatch 的意思是:Nginx 加载的私钥和它加载的证书不是一对。 Nginx 用 ssl_certificate 文件里的第一张证书和私钥比对,所以原因要么是私钥文件不对,要么是证书链文件的顺序不对。
快速修复
修法是找到和证书配对的私钥,或者把站点证书调到证书文件的最前面。先分别取出证书里的公钥和由私钥推导出的公钥,比较哈希:
openssl x509 -in /etc/nginx/ssl/example.com.crt -noout -pubkey | openssl sha256
openssl pkey -in /etc/nginx/ssl/example.com.key -pubout | openssl sha256
- 哈希不同: 找到属于这张证书的私钥,或者用新的 CSR 重新签发证书。
- 哈希相同: 说明你检查的这对没问题,要看 Nginx 实际引用的是哪个文件。执行
nginx -T | grep -E "ssl_certificate(_key)?"列出所有路径,包括其他server块和 include 进来的配置。
然后测试并重载:
sudo nginx -t && sudo systemctl reload nginx
原因
最常见的是前两种:私钥来自另一次 CSR 或续期,证书链文件里中间证书放在了前面。
1. 私钥属于另一张证书或另一次 CSR
常见场景:CSR 生成了两次,证书按第二次签发,却用了第一次的私钥;不同续期批次的文件混在一起;把别的域名的私钥复制了过来。
- 确认: 上面两个哈希不同。用同样方法检查其他候选私钥;下单时用的 CSR 里也有公钥:
openssl req -in example.com.csr -noout -pubkey | openssl sha256。证书和 CSR 也可以放进密钥匹配工具比对。 - 修复: 换成匹配的私钥。一个都对不上,就用 CSR 生成工具 或 OpenSSL 重新生成私钥和 CSR,请 CA 重签。
2. 证书链文件里中间证书在前
Nginx 把 ssl_certificate 文件里的第一张证书当作站点证书,后面的才是证书链。如果先拼了 CA 证书包,Nginx 就会拿你的私钥去和中间证书的公钥比对。
- 确认:
openssl x509 -in fullchain.crt -noout -subject显示的是 CA 的名字,而不是你的域名。 - 修复: 按“站点证书在前”重新拼接:
cat example.com.crt ca-bundle.crt > fullchain.crt
也可以用证书链补全工具生成顺序正确的文件。
3. 路径写反或指向了旧副本
- 确认:
nginx -T显示ssl_certificate_key指向旧的备份目录,或者指向了证书文件。 - 修复: 两条指令都指向当前这一对文件;Let's Encrypt 用
live/目录下的软链接,它们始终指向匹配的一对。
4. 粘贴时混入了多余内容
在 Windows 记事本或网页面板里复制粘贴,可能把两段 PEM 粘到一起,或者少了一行。
- 确认:
openssl x509 -in 文件 -noout或openssl pkey -in 文件 -noout解析失败。 - 修复: 从 CA 或面板重新下载文件,必要时用
dos2unix转换换行符。
各环境修法
不管用宝塔、Certbot 还是 Kubernetes,私钥和证书都要来自同一次签发,证书文件里站点证书排在最前。
宝塔面板: 在“网站 → SSL → 其他证书”里,“密钥(KEY)”一栏粘贴私钥,“证书(PEM格式)”一栏粘贴站点证书加中间证书,站点证书必须在最前面。从阿里云、腾讯云下载证书时选“Nginx”类型,压缩包里的 .key 和 .pem(或 .crt)是一对,不要和别的批次混用。
Let's Encrypt(Certbot):
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
acme.sh: 不要让 Nginx 直接读 ~/.acme.sh/ 下的文件;用 acme.sh --install-cert -d example.com --key-file ... --fullchain-file ... --reloadcmd "systemctl reload nginx",每次续期都会把两个文件一起复制过去。
商业证书: CA 会分别发站点证书和 CA 证书包,拼接时站点证书在前。私钥是你生成 CSR 时一起生成的那个,通常在服务器上或者生成 CSR 的平台里。
ECDSA + RSA 双证书: 配置两组 ssl_certificate / ssl_certificate_key 时,每个私钥要紧跟它自己的证书,顺序一一对应。
Docker / Kubernetes: TLS Secret 里的 tls.crt 和 tls.key 也必须匹配,用 kubectl get secret ... -o jsonpath 取出来后用同样的命令检查。
不要这样做
这 3 种做法修不好问题,还可能让损失扩大。
- 给现有证书“重新生成一个私钥”。 不可能匹配,反而可能覆盖掉某处还保存着的正确私钥。
- 不执行
nginx -t就重启。 启动失败会让服务器上所有网站一起下线,不只是这一个。 - 把私钥通过微信、邮件发给帮你排查的人。 发公钥哈希就够了;私钥一旦外泄,应该用新私钥重新签发证书。
常见问题
私钥丢了,能重新生成一个和证书匹配的吗?
不能。证书里写死了一个特定的公钥,只有当初一起生成的那个私钥才匹配。私钥丢了就重新生成私钥和 CSR,让 CA 重新签发;大多数 CA 在有效期内免费重签,阿里云、腾讯云控制台里也有“重新签发”入口。
能把私钥贴到在线工具里比对吗?
尽量不要。用“快速修复”里的命令在服务器本地比对公钥哈希即可;密钥匹配工具在浏览器本地运行、不上传任何内容,也可以用;但私钥最好始终不离开服务器。
为什么一续期就出这个错?
大多是只换了新证书、私钥还是旧的(或者反过来)。会轮换私钥的 ACME 客户端每次同时写两个文件,复制时要一起复制,或者直接让 Nginx 指向客户端的 live 路径(/etc/letsencrypt/live/<名称>/fullchain.pem 和 privkey.pem)。
这个错误会让整个 Nginx 挂掉吗?
restart 时会,reload 时不会。nginx -t 不通过,reload 被拒绝,原来正在运行的配置还能继续工作;但如果直接 restart,Nginx 会起不来,服务器上所有网站一起停。所以每次都先执行 nginx -t。