跳到主要内容
ecdsa.com
curl (OpenSSL backend)curl

错误信息

curl: (60) SSL certificate problem: unable to get local issuer certificate

也可能输出为

  • curl: (60) SSL certificate OpenSSL verify result: unable to get local issuer certificate (20)
  • SSL certificate problem: unable to get local issuer certificate

基于 OpenSSL 的 curl(Linux、macOS Homebrew、Git for Windows)打印的是这段文本;较新的版本(我们测试的是 8.22.0,而 8.14.1 仍使用上面的文本)会写作“SSL certificate OpenSSL verify result: unable to get local issuer certificate (20)”,附上 OpenSSL 的验证错误码。Windows 上用 Schannel 构建的 curl 会把同样的情况报告为“schannel: SEC_E_UNTRUSTED_ROOT (0x80090325)”,基于 GnuTLS 的 libcurl(Debian 和 Ubuntu 上的 git 使用它)则报告为“server verification failed: certificate signer not trusted”。基于 libcurl 构建的工具(例如 PHP 和 git)会打印“(60) ”之后的部分,并加上自己的前缀。

curl (OpenSSL backend) — CURLE_PEER_FAILED_VERIFICATION, X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY (20)

含义

curl 收到了服务器的证书,但在自己的 CA 证书包中找不到证书链最后一张证书的颁发者证书。这张证书本身可能完全有效;只是 curl 无法把它与任何受信任的根证书关联起来,因此拒绝连接(退出码 60)。缺失的环节要么是服务器忘记发送的中间证书,要么是你的机器上没有的根 CA。

原因

修复方法

  1. 1.

    查看服务器实际发送的内容

    列出握手中的证书。如果只出现一张(深度 0),且其颁发者是中间 CA,说明服务器配置不完整,必须在服务器上修复。如果证书链终止于公司根证书或自建根证书,说明客户端缺少该根证书。

    bash
    # Which certificates does the server send, and who issued the last one?
    openssl s_client -connect api.example.com:443 -servername api.example.com -showcerts </dev/null 2>/dev/null \
      | grep -E '^ *[0-9]+ s:|^ *i:'
    #  0 s:CN=api.example.com
    #    i:C=US, O=Example Trust Services, CN=Example TLS Issuing CA   <- leaf only: intermediate missing
  2. 2.

    修复服务器:提供完整证书链

    为 Web 服务器配置一个证书包,其中先是叶子证书,后面依次跟着所有中间证书。这样可以一次性为所有客户端修复这个错误,包括你无法控制的移动应用和 API 客户端。

    bash
    # Leaf first, then each intermediate in order; the root itself is optional
    cat api.example.com.crt intermediate.crt > fullchain.pem
    
    # nginx:   ssl_certificate     /etc/nginx/tls/fullchain.pem;
    # Apache:  SSLCertificateFile  /etc/apache2/tls/fullchain.pem
    # certbot: use live/<name>/fullchain.pem, never cert.pem
  3. 3.

    将私有根 CA 安装到系统证书存储

    如果证书链本来就应该终止于公司根证书或内部根证书,就把该根证书添加到操作系统的证书包中。从 IT 团队或平台团队获取根证书,或者直接从 CA 获取;切勿从失败连接所出示的证书中获取,因为那正是你目前还无法信任的东西。

    bash
    # Debian, Ubuntu, Alpine (the file must end in .crt)
    sudo cp corp-root-ca.pem /usr/local/share/ca-certificates/corp-root-ca.crt
    sudo update-ca-certificates
    
    # RHEL, Fedora, Amazon Linux
    sudo cp corp-root-ca.pem /etc/pki/ca-trust/source/anchors/
    sudo update-ca-trust
  4. 4.

    为单条命令或单个 shell 指定 curl 使用的证书包

    --cacert 只为单次调用添加信任,不改动系统;CURL_CA_BUNDLE 为该环境中的每次 curl 调用设置证书包。该文件可以包含多张 PEM 证书。

    bash
    curl --cacert corp-root-ca.pem https://api.example.com/
    
    # or for every curl command in this shell (a file may hold several PEM certificates)
    export CURL_CA_BUNDLE=/opt/certs/corp-and-public-roots.pem
  5. 5.

    在精简容器中安装 CA 证书包

    如果在容器内连公共网站都失败,说明镜像中没有证书包。在镜像中安装 ca-certificates(或从某个构建阶段复制 /etc/ssl/certs/ca-certificates.crt),而不是关闭验证。

    bash
    # Debian/Ubuntu-based images
    apt-get update && apt-get install -y --no-install-recommends ca-certificates
    # Alpine
    apk add --no-cache ca-certificates
  6. 6.

    临时诊断:确认是信任问题

    -k(--insecure)会禁用证书验证,因此成功的响应只能证明网络路径是通的,失败与信任有关。切勿把它留在脚本中:使用 -k 时,传输路径上的任何人都能读取和篡改流量,包括凭证和下载的代码。

    bash
    # Diagnostic only: if this works, the problem is trust, not the network.
    curl -sS -k -o /dev/null -w '%{http_code}\n' https://api.example.com/

相关错误

浏览完整的签名错误库