編集この回答はずっと前に受け入れられ、3回も賛成されましたが、(少なくとも部分的に)間違っていたので、この例外についてもう少し詳しく説明します。ご迷惑をおかけして申し訳ございません。
javax.net.ssl.SSLPeerUnverifiedException: ピアが認証されていません
これは通常、リモート サーバーが証明書をまったく送信しなかった場合にスローされる例外です。ただし、このバージョンでの実装方法と実装方法が原因で、Apache HTTP クライアントを使用するときに発生するエッジ ケースがありsun.security. ssl.SSLSocketImpl.getSession()
ます。
Apache HTTP クライアントを使用している場合、リモート証明書が信頼されていない場合にもこの例外がスローされ、より頻繁に " sun.security.validator.ValidatorException: PKIX path building failed
" がスローされます。
これが発生する理由は、Apache HTTP クライアントが他の処理SSLSession
を行う前に およびピア証明書を取得しようとするためです。
念のために言っておきますが、 でハンドシェイクを開始するには 3 つの方法がありますSSLSocket
。
- 明示的にハンドシェイクを開始する startHandshake を呼び出す、または
- このソケットでアプリケーション データを読み書きしようとすると、暗黙的なハンドシェイクが発生します。
- getSession の呼び出しは、現在有効なセッションがない場合にセッションのセットアップを試み、暗黙的なハンドシェイクが行われます。
以下に 3 つの例を示します。これらはすべて、信頼されていない証明書を持つホストに対するものです ( javax.net.ssl.SSLSocketFactory
Apache ではなく を使用)。
例 1:
SSLSocketFactory ssf = (SSLSocketFactory) sslContext.getSocketFactory();
SSLSocket sslSocket = (SSLSocket) ssf.createSocket("untrusted.host.example",
443);
sslSocket.startHandshake();
これは " " をスローjavax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed
します (予想どおり)。
例 2:
SSLSocketFactory ssf = (SSLSocketFactory) sslContext.getSocketFactory();
SSLSocket sslSocket = (SSLSocket) ssf.createSocket("untrusted.host.example",
443);
sslSocket.getInputStream().read();
これも " " をスローjavax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed
します (予想どおり)。
例 3:
SSLSocketFactory ssf = (SSLSocketFactory) sslContext.getSocketFactory();
SSLSocket sslSocket = (SSLSocket) ssf.createSocket("untrusted.host.example",
443);
SSLSession sslSession = sslSocket.getSession();
sslSession.getPeerCertificates();
ただし、これは をスローしjavax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
ます。
これは、バージョン 4.2.1 で使用されるApache HTTP クライアントAbstractVerifier
に実装されたロジックです。org.apache.http.conn.ssl.SSLSocketFactory
それ以降のバージョンでは、問題 HTTPCLIENT-1346 のレポートに基づいて、への明示的な呼び出しがstartHandshake()
行われます。
これは、呼び出し時に(内部メソッド) をスローするsun.security. ssl.SSLSocketImpl.getSession()
潜在的な s を、それ以上スローせずにキャッチする の実装に最終的に由来するようです。これはバグである可能性がありますが、これはセキュリティに大きな影響を与えるべきではありません。IOException
startHandshake(false)
SSLSocket
例 4:
SSLSocketFactory ssf = (SSLSocketFactory) sslContext.getSocketFactory();
SSLSocket sslSocket = (SSLSocket) ssf.createSocket("untrusted.host.example",
443);
SSLSession sslSession = sslSocket.getSession();
// sslSession.getPeerCertificates();
sslSocket.getInputStream().read();
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed
ありがたいことに、実際にそれを使用しようとすると、これはまだ " " をスローしますSSLSocket
(ピア証明書を取得せずにセッションを取得することによる抜け穴はありません)。
これを修正する方法
信頼されていない証明書に関する他の問題と同様に、使用しているトラスト ストアに必要なトラスト アンカー (つまり、検証しようとしているチェーンを発行した CA 証明書、または場合によっては実際のサーバー) が含まれていることを確認する必要があります。例外的な場合の証明書)。
これを修正するには、CA 証明書 (またはサーバー証明書自体) を信頼ストアにインポートする必要があります。あなたはこれを行うことができます:
- JRE トラスト ストア、通常は
cacerts
ファイル (その JRE を使用するすべてのアプリケーションに影響するため、必ずしも最適とは限りません)。
-Djavax.net.ssl.trustStore=...
トラスト ストア (オプションを使用して設定できます) のローカル コピーで、
SSLContext
その接続に固有のものを作成することにより(この回答で説明されているように)。(何もしないトラスト マネージャーを使用することを提案する人もいますが、これにより、接続が MITM 攻撃に対して脆弱になります。)
最初の答え
javax.net.ssl.SSLPeerUnverifiedException: ピアが認証されていません
これは、証明書を信頼することや、カスタムを作成する必要があることとは関係ありませんSSLContext
。これは、サーバーが証明書をまったく送信していないためです。
このサーバーは、明らかに TLS を適切にサポートするように構成されていません。これは失敗します (リモート証明書を取得できません):
openssl s_client -tls1 -showcerts -connect appserver.gtportalbase.com:443
ただし、SSLv3 は機能しているようです。
openssl s_client -ssl3 -showcerts -connect appserver.gtportalbase.com:443
このサーバーを実行しているユーザーがわかっている場合は、そのサーバーに連絡してこの問題を解決することをお勧めします。サーバーは、少なくとも最近では TLSv1 を実際にサポートする必要があります。
その間、この問題を解決する 1 つの方法は、独自のものを作成し、org.apache.http.conn.ssl.SSLSocketFactory
それを Apache Http クライアントとのこの接続に使用することです。
このファクトリはSSLSocket
、通常どおり、sslSocket.setEnabledProtocols(new String[] {"SSLv3"});
そのソケットを返す前に use を作成して、TLS を無効にする必要があります。そうしないと、TLS はデフォルトで有効になります。