60秒で解決
TLSハンドシェイクがalert unknown caで失敗した場合、受信側は本質的に「あなたの証明書は確認できますが、誰がそれを保証したのか分かりません」と言っています。これは通常、証明書を受け取る側のマシンが、署名した認証局(CA)を認識していない場合に発生します。
これは、mTLS(相互TLS)の設定中や、内部APIで自己署名証明書を使用している際によく発生します。
- クライアント側:
--cacertを使用してCAバンドルを明示的に渡すか、システムのトラストストアにCAをインストールします。 - サーバー側(mTLS): Nginxの
ssl_client_certificateまたはApacheのSSLCACertificateFileが、ユーザー証明書を署名した実際のルートCAを指しているか確認してください。 - テスト目的のみ:
curl -kを使用してチェックをバイパスしますが、本番環境では絶対に行わないでください。
なぜ接続が失敗するのか?
curl: (35) OpenSSL SSL_connect: alert unknown caというエラーメッセージが表示された場合、ハンドシェイクが失敗しています。一方のピアがもう一方に「致命的なアラート(Fatal Alert)」を送信しました。具体的には、unknown_caは証明書を受信したものの、ローカルのトラストストアにルートCAがないため、信頼の鎖(Chain of Trust)が切れていることを意味します。
標準的なHTTPSでは、ブラウザやcurlがサーバーを検証します。しかし、ポート8443などで高セキュリティなAPIによく使われるmTLS設定では、双方がお互いを検証する必要があります。クライアント証明書を送信中にこのエラーが発生した場合、サーバー側が発行者を認識できないために接続を拒否しています。
一般的なシナリオにおける具体的な解決策
1. システムのトラストストアの更新 (Linux)
内部ツールではプライベートCAがよく使用されます。ローカルマシンが会社の内部CAを信頼していない場合、OpenSSLがエラーを吐かないように手動で追加する必要があります。
Ubuntu/Debian:
# CAファイルを信頼されたフォルダに移動
sudo cp internal-ca.crt /usr/local/share/ca-certificates/internal-ca.crt
# 証明書ストアを再構築
sudo update-ca-certificates
CentOS/RHEL 7 & 8:
# アンカーディレクトリにファイルをコピー
sudo cp internal-ca.crt /etc/pki/ca-trust/source/anchors/
# システムの信頼設定を更新
sudo update-ca-trust extract
2. mTLSサーバー設定の修正
あなたが管理者の場合、クライアントにこのアラートが表示されているなら、サーバー側に彼らの身元を検証するためのバンドルが不足している可能性があります。たとえ有効なクライアント証明書であっても、サーバー側に署名者のルートCAがなければ意味がありません。
Nginxの設定:
server {
listen 443 ssl;
ssl_certificate /etc/nginx/certs/server.crt;
ssl_certificate_key /etc/nginx/certs/server.key;
# 重要:これはクライアント証明書を署名したCAである必要があります
ssl_client_certificate /etc/nginx/certs/client-ca.crt;
ssl_verify_client on;
}
Apacheの設定:
<VirtualHost *:443>
SSLEngine on
SSLCertificateFile /etc/apache2/ssl/server.crt
SSLCertificateKeyFile /etc/apache2/ssl/server.key
# クライアント証明書の署名を許可するCAを指す
SSLCACertificateFile /etc/apache2/ssl/client-ca.crt
SSLVerifyClient require
</VirtualHost>
3. Curlに直接CAバンドルを指定する
システム全体のストアを変更することが常に可能とは限りません。特に制限の厳しいCI/CDパイプラインなどではそうです。代わりに、curlに必要なファイルを直接指定します。標準的な接続には以下を使用します:
curl --cacert path/to/ca-bundle.crt https://api.internal.dev
mTLS接続の場合は、証明書、秘密鍵、およびサーバーを検証するCAを指定する必要があります:
curl --cacert server-ca.crt \
--cert client.crt \
--key client.key \
https://mtls.example.com:8443
OpenSSLによる詳細な診断
Curlの出力は曖昧な場合があります。ハンドシェイクがどこで止まっているのかを正確に把握するには、s_clientツールを使用します。これにより、証明書のやり取りを詳細に確認できます。
openssl s_client -connect your-server.com:443 -CAfile my-ca.crt
出力からVerificationステータスを探します:
Verification: OK: すべてが正常に動作しています。self signed certificate in certificate chain: ストアにルートCAが不足している可能性があります。unable to get local issuer certificate: 中間CAが見つかりません。フルチェーンが必要です。
避けるべき一般的な落とし穴
- 中間CAの欠如: 証明書はルートから直接ではなく、中間CAによって署名されることが多いことを忘れがちです。CAバンドルにフルチェーン(中間CA + ルートCA)が含まれていることを確認してください。
- 形式の問題: OpenSSLはPEM形式(Base64)を推奨します。CAがバイナリ形式の
.cerまたは.derファイルの場合は、変換してください:openssl x509 -inform der -in cert.cer -out cert.pem - 権限拒否: ウェブサーバーを実行しているユーザー(
www-dataやnginxなど)が、実際に.crtおよび.keyファイルの読み取り権限を持っていることを確認してください。証明書にchmod 644を設定するのが無難です。

