跳到主要内容
ecdsa.com
Ruby openssl (Net::HTTP, Faraday, HTTParty, RubyGems)Ruby

错误信息

state=error: certificate verify failed (unable to get local issuer certificate)

也可能输出为

  • OpenSSL::SSL::SSLError: SSL_connect returned=1 errno=0 peeraddr=127.0.0.1:8443 state=error: certificate verify failed (unable to get local issuer certificate)

完整消息是“SSL_connect returned=1 errno=0 peeraddr=<ip>:<port> state=error: certificate verify failed (unable to get local issuer certificate)”;在我们的测试中,openssl gem 3.0.1 到 4.0.2 的消息中都包含 peeraddr。Faraday 把它包装为消息相同的 Faraday::SSLError;直接调用 Net::HTTP 的库则会原样抛出 OpenSSL::SSL::SSLError。

Ruby openssl (Net::HTTP, Faraday, HTTParty, RubyGems) — OpenSSL::SSL::SSLError from SSL_connect

含义

Ruby 的 OpenSSL 绑定(Net::HTTP 使用它,因此大多数 Ruby HTTP 客户端也使用它)在已加载的 CA 中找不到服务器证书链的颁发者。括号中的文本是 OpenSSL 的验证原因;它前面的所有内容描述的是失败发生在 SSL_connect 的哪个环节。通常是 CA 为私有 CA 或位于代理之后,或者这个 Ruby 查找的 CA 证书包中没有它。

原因

修复方法

  1. 1.

    把根证书安装到 Ruby 的 OpenSSL 查找的位置

    OpenSSL::X509::DEFAULT_CERT_FILE 和 DEFAULT_CERT_DIR 给出了对应的文件和目录路径;在 Debian 和 Ubuntu 上,update-ca-certificates 会更新其中的证书。

    bash
    # Ruby's OpenSSL reads the system bundle (see its default paths)
    ruby -ropenssl -e 'p OpenSSL::X509::DEFAULT_CERT_FILE, OpenSSL::X509::DEFAULT_CERT_DIR'
    sudo cp corp-root-ca.pem /usr/local/share/ca-certificates/corp-root-ca.crt && sudo update-ca-certificates
  2. 2.

    为进程设置 SSL_CERT_FILE

    OpenSSL 会遵循 SSL_CERT_FILE 和 SSL_CERT_DIR;把它们指向同时包含公共根证书和你的 CA 的证书包。

    bash
    # One process / environment: point OpenSSL at a bundle with the needed roots
    export SSL_CERT_FILE=/etc/ssl/private-ca/corp-ca-bundle.pem
    ruby app.rb
  3. 3.

    为单个客户端提供独立的证书存储

    先调用 set_default_paths 再调用 add_file 的 OpenSSL::X509::Store,既保留公共根证书,又加入私有 CA,并保持完整验证。

    ruby
    # One client: default roots plus a private CA
    store = OpenSSL::X509::Store.new
    store.set_default_paths
    store.add_file("/etc/ssl/private-ca/corp-root-ca.pem")
    
    http = Net::HTTP.new("api.example.com", 443)
    http.use_ssl = true
    http.cert_store = store
  4. 4.

    Faraday 等封装库:传入 ca_file 或 cert_store

    大多数 Ruby HTTP 库都接受 ssl 选项,这些选项对应 Net::HTTP 的 ca_file、ca_path 或 cert_store。

    ruby
    conn = Faraday.new("https://api.example.com", ssl: { ca_file: "/etc/ssl/private-ca/corp-root-ca.pem" })

相关错误

浏览完整的签名错误库