本文へスキップ
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)はこのテキストを出力します。新しいリリース(ecdsa.com のテストでは 8.22.0。8.14.1 ではまだ上記のテキストでした)では、OpenSSL の検証コードが付いた「SSL certificate OpenSSL verify result: unable to get local issuer certificate (20)」という表記になります。Windows で Schannel を使ってビルドされた curl は同じ状況を「schannel: SEC_E_UNTRUSTED_ROOT (0x80090325)」と報告し、GnuTLS ベースの libcurl(Debian や Ubuntu の git が使用)は「server verification failed: certificate signer not trusted」と報告します。PHP や git など libcurl を利用するツールは、「(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.

    サーバーが実際に送信しているものを確認する

    ハンドシェイクに含まれる証明書を一覧表示してください。1 つしか表示されず(depth 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 をシステムのストアにインストールする

    チェーンが正当な理由で社内または組織内部のルートで終わっている場合は、そのルートを OS のバンドルに追加してください。ルートは 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.

    1 つのコマンドまたは 1 つのシェルに限って curl にバンドルを指定する

    --cacert はシステムに手を加えずに 1 回の呼び出しに限って信頼を追加し、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/

関連するエラー

署名エラーデータベースをすべて見る