跳到主要内容
ecdsa.com
PHPPHP

错误信息

cURL error 60: SSL certificate problem: unable to get local issuer certificate

也可能输出为

  • GuzzleHttp\Exception\ConnectException: cURL error 60: SSL certificate problem: unable to get local issuer certificate (see https://curl.se/libcurl/c/libcurl-errors.html) for https://api.example.com:8443/

Guzzle 把它格式化为“cURL error 60: SSL certificate problem: <reason> (see https://curl.se/libcurl/c/libcurl-errors.html) for <url>”;较早的 Guzzle 版本链接的是 https://curl.haxx.se/libcurl/c/libcurl-errors.html 页面。“problem:”之后的原因各不相同(“self-signed certificate in certificate chain”、“certificate has expired”)。直接调用 curl_error() 返回的是“cURL error 60: ”之后的文本。

PHP — Guzzle (and Laravel HTTP client, Symfony HttpClient on curl) over ext-curl; curl_errno() 60

含义

PHP 的 curl 扩展(Guzzle、Laravel HTTP 客户端以及 Symfony HttpClient 的 curl 传输层都使用它)无法验证服务器的证书:libcurl 报出错误 60,OpenSSL 给出的原因是“unable to get local issuer certificate”。libcurl 在自己的 CA 证书包中找不到签发该服务器证书链的 CA。在 Windows 上,通常是根本没有配置证书包;在其他系统上,则是 CA 为私有 CA,或者服务器没有发送中间证书。

原因

修复方法

  1. 1.

    配置 curl.cainfo 和 openssl.cafile

    在 php.ini 中同时设置这两项,让 ext-curl 和 PHP 流使用同一个证书包;注意 CLI 和 PHP-FPM 加载的常常是不同的 php.ini 文件。

    ini
    ; php.ini — CA bundle for ext-curl (Guzzle, Laravel Http, Symfony HttpClient) and for streams
    curl.cainfo = "/etc/ssl/certs/ca-certificates.crt"
    openssl.cafile = "/etc/ssl/certs/ca-certificates.crt"
    ; Windows: download cacert.pem from https://curl.se/docs/caextract.html and use its full path
  2. 2.

    Guzzle:为 verify 传入 CA 证书包

    verify 接受 CA 文件的路径;Guzzle 会检查该文件是否存在,不存在时抛出“SSL CA bundle not found”。

    php
    // Trust a private CA for one client: verify takes a CA bundle path
    $client = new GuzzleHttp\Client(['verify' => '/etc/ssl/private-ca/corp-root-ca.pem']);
    $response = $client->get('https://api.example.com/');
  3. 3.

    将 CA 安装到系统证书存储

    在 Linux 上,PHP 的 curl 和 OpenSSL 默认使用发行版的证书包;openssl_get_cert_locations() 可以显示这些路径。

    bash
    # Debian/Ubuntu-based PHP images: install the root, then check what PHP uses
    cp corp-root-ca.pem /usr/local/share/ca-certificates/corp-root-ca.crt && update-ca-certificates
    php -r 'var_dump(openssl_get_cert_locations()); echo ini_get("curl.cainfo"), PHP_EOL;'
  4. 4.

    检查实际生效的是哪个 php.ini

    php --ini 会列出已加载的文件;写进错误文件(CLI、FPM 和 Apache 各自使用不同的文件)的设置不会生效。

    bash
    php --ini                        # which php.ini files are loaded (CLI and FPM differ!)
    php -i | grep -E 'curl.cainfo|openssl.cafile'

相关错误

浏览完整的签名错误库