# key values mismatch：Nginx 证书和私钥不匹配

> Nginx 报 SSL_CTX_use_PrivateKey ... key values mismatch，说明私钥不属于 ssl_certificate 文件里的第一张证书。对比公钥哈希，找到原因再修。

Source: https://ssl.sh/zh/errors/nginx-key-values-mismatch/
Last updated: 2026-11-10

**`key values mismatch` 的意思是：Nginx 加载的私钥和它加载的证书不是一对。** Nginx 用 `ssl_certificate` 文件里的第一张证书和私钥比对，所以原因要么是私钥文件不对，要么是证书链文件的顺序不对。

## 快速修复

修法是找到和证书配对的私钥，或者把站点证书调到证书文件的最前面。先分别取出证书里的公钥和由私钥推导出的公钥，比较哈希：

```bash
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 进来的配置。

然后测试并重载：

```bash
sudo nginx -t && sudo systemctl reload nginx
```

## 原因

最常见的是前两种：私钥来自另一次 CSR 或续期，证书链文件里中间证书放在了前面。

### 1. 私钥属于另一张证书或另一次 CSR

常见场景：CSR 生成了两次，证书按第二次签发，却用了第一次的私钥；不同续期批次的文件混在一起；把别的域名的私钥复制了过来。

- **确认：** 上面两个哈希不同。用同样方法检查其他候选私钥；下单时用的 CSR 里也有公钥：`openssl req -in example.com.csr -noout -pubkey | openssl sha256`。证书和 CSR 也可以放进[密钥匹配工具](https://ssl.sh/zh/tools/key-matcher/)比对。
- **修复：** 换成匹配的私钥。一个都对不上，就用 [CSR 生成工具](https://ssl.sh/zh/tools/csr-generator/) 或 OpenSSL 重新生成私钥和 CSR，请 CA 重签。

### 2. 证书链文件里中间证书在前

Nginx 把 `ssl_certificate` 文件里的第一张证书当作站点证书，后面的才是[证书链](https://ssl.sh/zh/glossary/certificate-chain/)。如果先拼了 CA 证书包，Nginx 就会拿你的私钥去和中间证书的公钥比对。

- **确认：** `openssl x509 -in fullchain.crt -noout -subject` 显示的是 CA 的名字，而不是你的域名。
- **修复：** 按“站点证书在前”重新拼接：

```bash
cat example.com.crt ca-bundle.crt > fullchain.crt
```

也可以用[证书链补全工具](https://ssl.sh/zh/tools/chain-builder/)生成顺序正确的文件。

### 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）：**

```nginx
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` 就重启。** 启动失败会让服务器上所有网站一起下线，不只是这一个。
- **把私钥通过微信、邮件发给帮你排查的人。** 发公钥哈希就够了；私钥一旦外泄，应该用新私钥重新签发证书。
