SSLでハンドシェークが実行できません原因と対策を詳しく解説

目次

はじめに

目的

このドキュメントは、SSLハンドシェイクが実行できない問題に関する調査結果をまとめています。接続が確立できないときに考えられる主な原因を整理し、それぞれに対する具体的な確認方法と対処手順を分かりやすく示します。

対象読者

システム管理者、アプリケーション開発者、運用担当者を主な対象とします。SSLや証明書に不慣れな方でも、手順に沿って確認できるよう配慮しています。

本書の構成と特徴

各章は原因ごとに分かれており、原因の説明、確認方法、対応策を順に示します。専門用語は必要最小限に留め、具体例やコマンド例を使って実務的に役立つ情報を提供します。

使い方

まずログやエラーメッセージを確認し、該当しそうな章から読み進めてください。手順を実行する前に環境のバックアップを取ることをおすすめします。問題が複数重なっている場合もあるため、順番どおりに確認すると見落としを減らせます。

注意点

ここで示す手順は一般的な対処法です。環境依存の違いがあるため、実行時は自分の環境に合わせて調整してください。証明書や鍵の操作は慎重に行ってください。

SSLハンドシェイクが実行できない原因と解決方法

概要

SSL/TLSハンドシェイクは、クライアントとサーバーが暗号化された通信を始めるための手順です。ここでは、ハンドシェイクが失敗する代表的な原因と、それぞれの解決方法を分かりやすく説明します。問題発生時はログを確認し、順に原因を潰していくことをおすすめします。

主な原因と対処法(一覧)

  1. 証明書がKeystoreにインポートされていない
  2. 問題: アプリケーションが必要とする証明書やルート証明書がKeystoreにないと信頼できず接続を拒否します。
  3. 対処: Javaなら keytoolで証明書をインポートします。例: keytool -importcert -file server.crt -keystore keystore.jks。インポート後にサービスを再起動してください。

  4. 自己署名証明書の使用

  5. 問題: 自己署名証明書は既定で信頼されません。
  6. 対処: テスト環境ならクライアントに自己署名証明書を手動で信頼させます。本番では公的な認証局から証明書を取得してください。

  7. 証明書チェーンの不完全

  8. 問題: 中間証明書が欠けるとブラウザやクライアントが検証できません。
  9. 対処: サーバーに中間証明書を含めたチェーンを設定します。提供元の指示に従ってフルチェーンを設置してください。

  10. プロトコルバージョンの不一致

  11. 問題: サーバーがTLS1.2のみ、クライアントがTLS1.0しか使えない等で握手が通りません。
  12. 対処: サーバー・クライアント双方でサポートするTLSバージョンを揃えるか、互換設定を追加します。

  13. クライアント証明書の問題

  14. 問題: 相互認証を要求するサーバーでクライアント証明書が無い、あるいは誤った場合に拒否されます。
  15. 対処: 正しいクライアント証明書を配置し、キーストアやパスワードを確認してください。

  16. 暗号スイートの互換性問題

  17. 問題: サーバーとクライアントで共通の暗号アルゴリズムが無いと成立しません。
  18. 対処: 両者で共通の暗号スイートを有効化するか、セキュアな互換設定を追加します。

  19. SSL証明書の有効期限切れ

  20. 問題: 期限切れの証明書は無効として扱われます。
  21. 対処: 証明書を更新し、新しい証明書を導入して再起動します。

  22. クライアントのシステムクロックの誤り

  23. 問題: 時刻が大きくずれていると証明書の有効期間の検証に失敗します。
  24. 対処: NTP等で時刻を同期してください。

  25. プロキシ設定の誤り

  26. 問題: プロキシがSSLを中継または遮断していると握手が失敗します。
  27. 対処: プロキシの設定を確認し、必要なら直接接続やプロキシの透過設定を行ってください。

チェックの順番(簡単な手順)

  1. ログを確認してエラー内容を把握する
  2. 証明書の有無・有効期限・チェーンを確認する
  3. キーストアやクライアント証明書の設定を確認する
  4. プロトコルと暗号スイートの互換性を確認する
  5. システム時刻とネットワーク(プロキシ)を確認する

各項目ごとに具体的なエラーメッセージや環境に基づいて対応を進めると、原因の特定が早まります。必要であれば、個別の原因ごとの詳細な手順もご案内します。

原因1:証明書がKeystoreにインポートされていない

概要

サーバー証明書がクライアント側の信頼済みKeystore(トラストストア)に含まれていないと、SSLハンドシェイクで相手を信用できずエラー(SSLHandshakeException)が発生します。特に自己生成や社内CA発行の証明書で起きやすい問題です。

なぜ起こるか

クライアントは接続先の証明書を検証し、信頼できる発行元(CA)が登録されているKeystoreにその証明書またはCAの証明書が無いと接続を拒否します。ブラウザでは警告が出ますが、プログラムでは例外になります。

具体的な解決手順(例:Javaのkeytoolを想定)

  1. サーバー証明書を取得します(例:サーバーから.pem/.crtをダウンロード、またはopenssl s_clientで取得)。
  2. クライアントで使用するKeystore(例:truststore.jks)にインポートします。例コマンド:
    keytool -import -alias myserver -file server.crt -keystore truststore.jks
  3. パスワードを求められます。既存のトラストストアを使う場合はバックアップを推奨します。
  4. アプリやサービスを再起動して新しいKeystoreを読み込ませます。

検証方法

  • keytool -list -v -keystore truststore.jks で証明書が登録されているか確認します。
  • 接続を再試行してSSLHandshakeExceptionが解消されたか確かめます。

よくあるミスと対策

  • 間違ったKeystoreを編集している(パスや環境ごとに別のファイルになることが多い):対象のアプリが参照するKeystoreを確認してください。
  • パスワードエラーやファイル権限:読み取り権限と正しいパスワードを確認します。
  • 中間CAをインポートしていない:サーバー証明書だけでなくチェーン全体が必要な場合があります。

注意点

  • 運用環境では証明書の有効期限やローテーション計画も考慮してください。
  • 自動化(構成管理ツール)で管理すると人的ミスを減らせます。

原因2:自己署名証明書の使用

問題の説明

自己署名証明書は発行者と所有者が同じ証明書です。テスト用途では手軽に使えますが、クライアントはその証明書を自動で信頼しません。信頼されないとSSLハンドシェイクで証明書検証が失敗します。

なぜ失敗するのか(かんたんな例)

ウェブブラウザやAPIクライアントは、証明書を信頼できる認証局(CA)の一覧と照らし合わせます。自己署名証明書はその一覧にありません。結果として「不明な発行者」や「証明書チェーンが不完全」といったエラーが出ます。

解決策

1) クライアントに自己署名証明書をインポートする(短期対応)
– テスト環境や限定された内部ネットワークで有効です。クライアントのKeystoreやOSの信頼ストアに証明書を追加します。
2) 信頼できる認証局から署名された証明書を使う(推奨)
– 公開サービスや本番環境では、CA署名の証明書を導入してください。自動更新やブラウザ互換性の点で安全です。

手順の例(簡潔)

  • クライアントKeystoreへインポート(Javaの例):
    keytool -importcert -file mycert.crt -keystore client-truststore.jks -alias myserver
  • サーバーにCA署名証明書を入れる流れ:
  • CSRを作成してCAに提出
  • 返却された証明書と中間CAをサーバーに配置
  • サーバー設定を更新して再起動

注意点

  • 社内で自己署名を使う場合、全クライアントに同じ手順を周知してください。
  • 証明書を手動で信頼させると、管理負荷やミスの原因になります。本番はできるだけCA署名に移行してください。

原因3:証明書チェーンの不完全

説明

サーバー証明書は単独では信頼されないことがあります。中間証明書(Intermediates)が抜けていると、クライアントは発行元まで遡れず「信頼できない」と判断します。これがSSLHandshakeExceptionの原因になります。

なぜ問題になるのか

多くのクライアントはルート証明書のみをローカルに持ちます。中間証明書はサーバーから渡される想定です。サーバーが中間を送らないと、チェーンが途中で切れ、接続が拒否されます。

確認方法

  • opensslを使う例:
    openssl s_client -connect example.com:443 -showcerts
    出力で中間証明書が表示されるか確認します。
  • ブラウザの鍵アイコン→証明書表示でもチェーンを確認できます。

解決手順(一般的な流れ)

  1. 発行元(CA)から中間証明書を入手します。
  2. サーバー証明書のファイルと中間証明書を結合します。順序は「サーバー証明書 → 中間証明書(複数順番に)」です。
    例: cat server.crt intermediate.crt > fullchain.crt
  3. Webサーバーの設定で結合ファイル(fullchain)を指定します。
  4. Nginx: ssl_certificate /path/to/fullchain.crt;
  5. Apache: SSLCertificateFile /path/to/fullchain.crt
  6. Java系(Keystore)ではチェーンを含めてインポートするか、PKCS#12で鍵とチェーンをまとめて作成します。
    例: keytool -importcert -file fullchain.crt -keystore keystore.jks

よくある誤りと注意点

  • ルート証明書をサーバー側に含める必要は通常ありません。クライアント側が持っているためです。
  • 証明書の順序が逆だと正しく読み込まれません。

上記を確認・修正すると、多くの証明書チェーン問題は解決します。

原因4:プロトコルバージョンの不一致

説明

クライアントとサーバーが使えるTLS(旧SSL)のバージョンが合わないと、最初の握手(ハンドシェイク)が成立しません。たとえば、クライアントがTLS 1.2以上のみ許可していて、サーバーがTLS 1.0しか提供しない場合に失敗します。

主な原因

  • サーバーが古いプロトコルを使うよう設定されている
  • クライアント側のライブラリやOSが古い/制限されている
  • ロードバランサーやプロキシが古いTLSで通信している

対処手順

  1. サーバーのTLS設定を確認し、TLS 1.2以上を有効にします。可能ならTLS 1.3を検討してください。安全性が高くなります。
  2. クライアントライブラリやミドルウェアを最新版にアップデートします。
  3. 中間に入る機器(ロードバランサー、プロキシ)の設定も合わせます。
  4. 必要があれば互換性のあるプロトコルのみを限定的に許可しますが、古いバージョンは避けます。

確認方法(簡単な例)

  • openssl: openssl s_client -connect example.com:443 -tls1_2
  • curl: curl -v –tlsv1.2 https://example.com

注意点

古いプロトコルを無理に許可すると安全性が落ちます。まずは最新のTLSをサポートする方向で対応してください。

原因5:クライアント証明書の問題

説明

サーバーがクライアント証明書を要求する設定になっていると、クライアントが適切な証明書を送れない場合にSSLハンドシェイクが失敗します。特に企業向けの相互認証やAPIの強化認証で起こりやすい問題です。

よくある症状

  • ブラウザで証明書選択のダイアログが出ない、または接続エラーになる
  • エラーログに “handshake failure” や “no certificate available” が出る
  • クライアント側で “unknown_ca” や “certificate required” といった警告が出る

確認手順(順序立てて)

  1. クライアントに証明書が存在するか確認する(ブラウザの証明書ストアやアプリのキーストア)。
  2. 秘密鍵が対応する証明書と一致しているかを確認する。鍵ファイルの権限もチェックします。
  3. 証明書形式を確認する。多くのツールではPKCS#12(.p12/.pfx)が使いやすいです。
    例:opensslでP12を作る
    openssl pkcs12 -export -in client.crt -inkey client.key -out client.p12 -name client
  4. 証明書の有効期限と失効(CRL/OCSP)を確認する。

クライアント側の設定例

  • curl: curl –cert client.p12:password –cert-type P12 https://example.com
  • Javaアプリ: -Djavax.net.ssl.keyStore=client.p12 -Djavax.net.ssl.keyStorePassword=pass

Apache(mod_ssl)での見直しポイント

  • SSLVerifyClient の設定を確認(none/optional/require)。テスト時は none や optional で切り分けします。
  • SSLCACertificateFile にクライアント証明書を発行したCAを指定し、SSLVerifyDepth を適切に設定します。
    例:
    SSLVerifyClient require
    SSLCACertificateFile /path/to/ca.pem
    SSLVerifyDepth 2

テスト方法

  • openssl s_client を使って手動で証明書を送ってみます(-cert と -key を指定)。
    openssl s_client -connect host:443 -cert client.crt -key client.key

ログの確認

サーバー(Apache)のエラーログやクライアントのデバッグ出力を見れば原因が分かることが多いです。証明書チェーンやCAの不一致、秘密鍵の欠如がよく見つかります。

原因6:暗号スイートの互換性問題

概要

クライアントとサーバーが共通に理解できる暗号方式(暗号スイート)がないと、SSL/TLSハンドシェイクは成立しません。つまり両者の『言葉』が一致しないため通信が始められません。

具体例

  • サーバーが新しいTLS1.3向けの暗号のみ許可しており、古いクライアントがそれに対応していない。
  • サーバーがRC4や3DESなど脆弱な暗号をまだ残し、現代のクライアントがそれらを拒否する。

対処方法(手順)

  1. サーバーの暗号スイート設定を確認します(Webサーバーの設定ファイルやロードバランサの画面)。
  2. 広く使われている安全な暗号を有効にします(例:TLS1.2/1.3、ECDHE、AES-GCM、ChaCha20-Poly1305)。
  3. 明らかに脆弱なもの(RC4、DES、3DES、NULL暗号など)は無効化します。
  4. 必要なら古いクライアントに対応するため、互換性のある安全な候補を順番に並べます(優先度を決める)。
  5. 設定変更後はサーバーを再起動し、検証します。

検証方法

  • openssl s_client や curl -v で接続テストを行います。
  • SSL Labsなどの外部診断ツールで互換性と安全性を確認します。

注意点

互換性を優先して古い弱い暗号を許可すると安全性が低下します。まずはクライアントのアップデートを促し、サーバー側はできるだけ安全な選択肢を提供してください。

原因7:SSL証明書の有効期限切れ

説明

SSL証明書は有効期間が決まっており、期限を過ぎるとブラウザやクライアントがその証明書を信頼しなくなります。結果としてTLSハンドシェイクが途中で止まり、接続が成立しません。

症状

  • ブラウザで「証明書が無効」や「接続は安全ではありません」と表示される
  • curlやAPIクライアントでHandshakeエラーや証明書エラーが返る

確認方法(簡単な例)

  • ブラウザでサイトを開き、証明書の有効期限を確認する
  • コマンド例:openssl s_client -connect example.com:443 -showcerts で表示される有効期限を確認
  • オンラインの証明書チェックツールを使う

更新手順(基本)

  1. 新しい証明書を発行または購入する(Let’s Encryptなど自動発行も可)
  2. サーバーやロードバランサーに新証明書と中間証明書をインストールする
  3. サービスを再起動または証明書リロードして反映させる

自動化の勧め

短期間で更新が必要な証明書は、自動更新を設定すると運用負荷が減ります。Let’s Encryptではcertbotなどを使って自動更新を構築します。

テストと注意点

  • 更新後にブラウザとコマンドで再確認してください
  • 中間証明書が欠けると期限切れでなくても信頼されないことがあります
  • サーバー複数台構成では全てに新証明書を反映してください

よくあるミス

  • 別の環境にある古い証明書を誤って配置する
  • 自動更新の設定を忘れて期限切れになる
  • キーストアやパスワードを間違えて読み込めない

上記を順に確認・実施すれば、期限切れによるTLSハンドシェイク失敗は解決できます。

原因8:クライアントのシステムクロックの誤り

説明

クライアント端末の時刻が正しくないと、サーバー証明書の有効期間(開始〜終了)と照合できず、TLS/SSLハンドシェイクが失敗します。たとえば端末が過去に戻っていると「まだ有効になっていない(not yet valid)」、未来に進んでいると「期限切れ(expired)」と判定されます。

影響

  • ブラウザやAPIクライアントで接続エラーが出ます。
  • 特定のサービスだけ接続できないことがあります(端末ごとの設定差)。

対処方法

  1. 自動時刻同期を有効にする(ネットワーク時刻:NTP)。
  2. 手動で時刻とタイムゾーンを正す。
  3. 長期間停止しているPCはCMOS電池切れを疑う。
  4. モバイルはキャリア/ネットワーク時刻を利用する。

確認手順(例)

  • 日付確認:Windowsなら「date /T」、Linux/macOSは「date」。
  • NTP同期:Windowsは「w32tm /resync」、Linuxは「timedatectl set-ntp true」や「ntpdate pool.ntp.org」。
  • 証明書の有効期間確認:opensslで「openssl s_client -connect example.com:443 -showcerts」やブラウザの証明書情報を参照。

注意点

アプリが内部で独自時刻を扱う場合は、その設定も確認してください。時刻を直した後、ブラウザやサービスを再起動して再接続を試みてください。

原因9:プロキシ設定の誤り

問題の説明

誤ったプロキシ設定があると、ブラウザやアプリが直接サーバーのポート443に接続できず、SSLハンドシェイクが失敗します。企業の透過プロキシや認証付きプロキシが中継するときに起きやすいです。

確認手順

  • 別のブラウザやプライベートウィンドウで接続を試す。
  • 別ネットワーク(スマホのテザリング等)で同じURLにアクセスする。
  • OSやブラウザのプロキシ設定を確認する(Windows: 設定→ネットワーク→プロキシ、macOS: システム設定→ネットワーク→プロキシ)。
  • CLIは環境変数(HTTP_PROXY/HTTPS_PROXY)を確認し、curlなどで–noproxyオプションを使って試す。

修正例と対処

  • 手動でプロキシを無効化して再試行する。即時効果があります。
  • 認証が必要なプロキシは正しいユーザー名・パスワードを設定する。
  • 企業プロキシが独自のルート証明書を注入している場合、証明書を信頼するかプロキシ経由を避ける。
  • アプリケーション固有の設定(Javaの-DproxyHostなど)も確認してください。

よくある原因

  • CONNECTメソッドを遮断している。
  • プロキシ認証でTLSセッションが確立できない。

チェックリスト(簡易)

  1. プロキシを一時的に無効化して確認
  2. 別ネットワークで動作確認
  3. 必要ならIT管理者へログ確認を依頼

上記を順に試せば、多くのプロキシ関連のSSL失敗は解消できます。

よかったらシェアしてね!
  • URLをコピーしました!

この記事を書いた人

目次