当前位置:首页 > 云服务器 > 正文

https自签名证书验证失败怎么办?如何配置信任自签名证书

在 HTTPS 通信中,浏览器或客户端默认要求服务器提供的 SSL/TLS 证书必须由受信任的证书颁发机构(CA)签发,在开发、测试或内部网络环境中,使用自签名证书(Self-Signed Certificate)是一种常见且低成本的选择,由于自签名证书未经过第三方 CA 验证,客户端在连接时会抛出“证书不受信任”或“SSL 握手失败”的错误。

要解决这一问题,核心思路是让客户端信任该自签名证书的公钥,以下是几种主流操作系统和编程环境下的详细验证与配置方法。

原理简述

HTTPS 的安全基础是公钥基础设施(PKI),当客户端连接服务器时:

  1. 服务器发送其证书(包含公钥和身份信息)。
  2. 客户端检查该证书是否由它信任的根 CA 签名。
  3. 对于自签名证书,服务器既是 CA 也是实体,因此客户端必须手动将该证书(或其公钥)添加到本地的“受信任根证书存储区”。

操作系统层面的配置

Windows 系统

Windows 用户需要将自签名证书导入到“受信任的根证书颁发机构”存储区。

  • 步骤
    1. 双击 .cer 或 .crt 文件。
    2. 点击“安装证书”。
    3. 选择“本地计算机”(需要管理员权限)或“当前用户”。
    4. 关键步骤:选择“将所有的证书都放入下列存储”,并浏览选择 “受信任的根证书颁发机构”
    5. 完成安装后,重启浏览器或应用程序。

macOS 系统

macOS 使用“钥匙串访问”(Keychain Access)管理证书。

  • 步骤
    1. 双击证书文件,将其添加到“登录”或“系统”钥匙串。
    2. 在钥匙串访问中双击该证书,展开“信任”部分。
    3. 将“使用此证书时”设置为 “始终信任”
    4. 系统可能会要求输入密码或指纹以确认更改。

Linux 系统 (Ubuntu/Debian/CentOS)

Linux 发行版通常使用 /usr/local/share/ca-certificates/ 或 /etc/pki/ca-trust/source/anchors/ 目录。

https自签名证书验证失败怎么办?如何配置信任自签名证书 第1张

  • Debian/Ubuntu 示例: sudo cp my-ca.crt /usr/local/share/ca-certificates/ sudo update-ca-certificates

  • CentOS/RHEL 示例: sudo cp my-ca.crt /etc/pki/ca-trust/source/anchors/ sudo update-ca-trust

编程语言与框架层面的配置

在许多编程场景中,直接修改操作系统证书库并不方便,因此通常通过代码或环境变量绕过验证。

Node.js

Node.js 使用 NODE_TLS_REJECT_UNAUTHORIZED 环境变量来全局控制 TLS 验证。

  • 方法 A:环境变量(全局生效,不推荐生产环境)

    export NODE_TLS_REJECT_UNAUTHORIZED=0 node app.js
  • 方法 B:代码级别指定(推荐)

    https自签名证书验证失败怎么办?如何配置信任自签名证书 第2张

    const https = require('https'); const fs = require('fs'); // 加载自签名证书 const options = { ca: fs.readFileSync('./path/to/self-signed-ca.crt') }; https.get('https://localhost:8443', options, (res) => { // 处理响应 });

Python (Requests 库)

Python 的 requests 库默认验证 SSL 证书。

  • 方法 A:禁用验证(仅用于测试) import requests response = requests.get('https://localhost:8443', verify=False) # 注意:这会忽略所有证书验证,存在中间人攻破风险
  • 方法 B:指定 CA 包(推荐) import requests response = requests.get('https://localhost:8443', verify='/path/to/self-signed-ca.crt')

Java (JDK)

Java 使用 cacerts 密钥库。

  • 方法:使用 keytool 导入证书。 keytool -import -alias myserver -file self-signed.crt -keystore $JAVA_HOME/lib/security/cacerts # 默认密码通常是 changeit

Go 语言

Go 语言默认严格验证证书。

  • 方法:使用 http.Client 自定义 TLSClientConfig。

    import ( "crypto/tls" "crypto/x509" "io/ioutil" "net/http" ) func main() { caCert, err := ioutil.ReadFile("self-signed.crt"

    https自签名证书验证失败怎么办?如何配置信任自签名证书 第3张

    ) if err != nil { panic(err) } caCertPool := x509.NewCertPool() caCertPool.AppendCertsFromPEM(caCert) client := &http.Client{ Transport: &http.Transport{ TLSClientConfig: &tls.Config{ RootCAs: caCertPool, }, }, } client.Get("https://localhost:8443") }

常见错误与排查表格

错误现象 可能原因 解决方案
SSL_ERROR_BAD_CERT_DOMAIN 证书中的 Common Name (CN) 或 SAN 与访问的域名/IP 不匹配 确保证书包含正确的域名或 IP 地址(建议使用 SAN 扩展)。
CERTIFICATE_VERIFY_FAILED 客户端未信任该自签名证书的根证书 将证书导入操作系统的“受信任根证书”存储,或在代码中指定 verify 路径。
UNABLE_TO_VERIFY_LEAF_SIGNATURE 证书链不完整,缺少中间证书 自签名证书通常不需要中间证书,但如果由内部 CA 签发,需确保包含完整的证书链。
浏览器仍提示不安全 浏览器缓存了旧的证书状态 清除浏览器 SSL 状态或重启浏览器;Chrome 需清除“已清除的证书”缓存。

安全建议

虽然上述方法可以解决验证问题,但请务必注意:

  1. 生产环境严禁使用自签名证书:自签名证书无法防止中间人攻破(MITM),因为攻破者可以生成自己的自签名证书并诱导用户信任。
  2. 测试环境最佳实践:在 CI/CD 或本地开发中,建议使用 mkcert 等工具生成由本地根 CA 签发的证书,并将其自动安装到系统信任库中,这样既方便又符合标准流程。
  3. 最小权限原则:如果必须在代码中禁用验证,请确保仅针对特定的测试请求或内部服务,并记录日志以便审计。


相关问题与解答

问题 1:为什么我在代码中指定了自签名证书路径,仍然报“证书不受信任”错误?

解答:

这通常是因为代码中指定的证书文件与服务器实际发送的证书不一致,或者证书格式不正确,请检查以下几点:

  1. :确保读取的 .crt 或 .pem 文件包含完整的证书链(如果是 CA 签发)或正确的自签名证书主体。
  2. 编码格式:确保证书是 PEM 格式(以 -----BEGIN CERTIFICATE----- 开头),如果是 DER 格式,需要先转换。
  3. 哈希匹配:在调试模式下,打印服务器发送的证书指纹(Fingerprint),并与你本地加载的证书指纹进行比对,确认两者完全一致。
  4. 环境变量干扰:检查是否设置了全局禁用验证的环境变量(如 NODE_TLS_REJECT_UNAUTHORIZED=0),这可能会覆盖代码中的配置逻辑。

问题 2:使用 mkcert 生成的本地证书,为什么在 Chrome 浏览器中仍然显示“不安全”?

解答:

mkcert 的工作原理是生成一个本地根 CA 并将其安装到操作系统的信任库中,Chrome 仍然报错,原因通常是:

  1. 未重启浏览器:Chrome 在启动时会加载操作系统的证书信任列表,安装证书后,必须完全关闭并重新打开 Chrome。
  2. 操作系统信任库未更新:mkcert 需要管理员/root 权限才能将 CA 安装到系统信任库,如果安装时出错,证书可能只存在于用户空间,而 Chrome 以不同权限运行,请重新运行 mkcert -install 并确保以管理员身份执行。
  3. Chrome 的严格模式:某些企业版 Chrome 或开启了特定安全策略的浏览器可能会忽略用户安装的根证书,可以尝试在 Chrome 地址栏输入 chrome://net-internals/#hsts,删除该域名的 HSTS 预加载记录,然后重试。
  4. 证书过期:mkcert 生成的证书默认有效期为一年,如果证书已过期,浏览器会拒绝连接,检查证书有效期并重新生成。

0