跳到主要内容
ecdsa.com
Node.js tls.checkServerIdentity()Node.js crypto 模块

错误信息

Hostname/IP does not match certificate's altnames: Host: api.example.com. is not in the cert's altnames: DNS:www.example.com

也可能输出为

  • Hostname/IP does not match certificate's altnames: IP: 127.0.0.1 is not in the cert's list:
  • code: 'ERR_TLS_CERT_ALTNAME_INVALID'
  • ERR_TLS_CERT_ALTNAME_INVALID

其中的主机和 altnames 列表按每次失败的实际情况填写。对于 IP 地址,消息文本为“Hostname/IP does not match certificate's altnames: IP: <address> is not in the cert's list: <entries>”。npm 将其显示为“npm error code ERR_TLS_CERT_ALTNAME_INVALID”。

Node.js tls.checkServerIdentity() — code ERR_TLS_CERT_ALTNAME_INVALID

含义

证书链是受信任的,但 tls.checkServerIdentity() 没有找到与 Node.js 所连接主机相匹配的主题备用名称(SAN)。消息列出了双方:你请求的主机,以及证书实际包含的名称,因此一眼就能看出哪里不匹配。如果证书带有 SAN 扩展,通用名称会被忽略。

原因

修复方法

  1. 1.

    列出证书中的名称

    把 subjectAltName 与你所连接的主机对比。

    bash
    echo | openssl s_client -connect api.example.com:443 -servername api.example.com 2>/dev/null \
      | openssl x509 -noout -ext subjectAltName
  2. 2.

    重新签发证书,包含所有正在使用的名称

    加入所有 DNS 名称;如果客户端通过地址连接,还要加入 IP 条目。

    bash
    openssl req -new -key server.key -out server.csr -subj "/CN=api.example.com" \
      -addext "subjectAltName=DNS:api.example.com,DNS:www.example.com,IP:10.0.0.5"
  3. 3.

    通过地址连接时设置 servername

    在 servername(以及 Host 请求头)中保留证书上的名称,然后连接到该地址;这样验证使用的是正确的名称,而不是被关闭。

    js
    // Connect to an address but verify the certificate's real name (also sent as SNI)
    import https from "node:https";
    
    https.get({ host: "10.0.0.5", port: 443, path: "/", servername: "api.example.com",
                headers: { host: "api.example.com" } }, (res) => res.resume());
  4. 4.

    用 checkServerIdentity 设置严格限定的例外

    对于证书暂时无法重新签发的旧主机,把一个已知别名映射到证书上的名称,再交给 tls.checkServerIdentity() 处理。切勿无条件返回 undefined:那样会彻底关闭名称检查。

    js
    // Narrow exception for a host you cannot reissue: accept one extra name, keep all other checks
    import https from "node:https";
    import tls from "node:tls";
    
    const agent = new https.Agent({
      checkServerIdentity: (host, cert) =>
        tls.checkServerIdentity(host === "legacy-alias.internal" ? "api.example.com" : host, cert),
    });

相关错误

浏览完整的签名错误库