ApacheHttpClientの使用4.2.1。フォームベースのログイン例からコピーしたコードを使用する
http://hc.Apache.org/httpcomponents-client-ga/examples.html
SSLで保護されたログインフォームにアクセスすると、例外が発生します。
Getting library items from https://appserver.gtportalbase.com/agileBase/AppController.servlet?return=blank
javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated
Closing http connection
at Sun.security.ssl.SSLSessionImpl.getPeerCertificates(SSLSessionImpl.Java:397)
at org.Apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.Java:128)
at org.Apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.Java:572)
at org.Apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.Java:180)
at org.Apache.http.impl.conn.ManagedClientConnectionImpl.open(ManagedClientConnectionImpl.Java:294)
at org.Apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.Java:640)
at org.Apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.Java:479)
at org.Apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.Java:906)
at org.Apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.Java:805)
at org.Apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.Java:784)
私が知る限り、証明書は問題なく(スタックトレースの前にURLを参照)、期限切れではありません-ブラウザは文句を言いません。
証明書をキーストアにインポートしてみました
Apache HttpClientで無効なSSL証明書を処理する方法は?
変更なし。カスタムSSLContextを作成して、Javaでエラーを無視するように強制できると思いますが、セキュリティホールを開きたくないので、根本原因を修正したいと思います。
何か案は?
[〜#〜] edit [〜#〜]この回答はずっと前に受け入れられ、3回も賛成されたと思いますが、 (少なくとも部分的に)正しくないので、この例外についてもう少し詳しく説明します。ご迷惑をおかけして申し訳ございません。
javax.net.ssl.SSLPeerUnverifiedException:ピアが認証されていません
これは通常リモートサーバーが証明書をまったく送信しなかった場合にスローされる例外です。ただし、このバージョンでの実装方法とSun.security. ssl.SSLSocketImpl.getSession()
の実装方法が原因で、ApacheHTTPクライアントを使用するときに発生するエッジケースがあります。
Apache HTTPクライアントを使用している場合、リモート証明書が信頼されていない場合にもこの例外がスローされ、「Sun.security.validator.ValidatorException: PKIX path building failed
」がスローされることがよくあります。
これが発生する理由は、ApacheHTTPクライアントが他のことを行う前にSSLSession
とピア証明書を取得しようとするためです。
念のために言っておきますが、 SSLSocket
でハンドシェイクを開始する3つの方法 :
- ハンドシェイクを明示的に開始するstartHandshakeを呼び出す、または
- このソケットでアプリケーションデータを読み書きしようとすると、暗黙的なハンドシェイクが発生します。
- getSessionの呼び出しは、現在有効なセッションがない場合にセッションのセットアップを試み、暗黙のハンドシェイクが実行されます。
以下に3つの例を示します。これらはすべて、信頼されていない証明書を持つホストに対して行われます(Apacheではなくjavax.net.ssl.SSLSocketFactory
を使用)。
例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の org.Apache.http.conn.ssl.SSLSocketFactory
で使用される ApacheHTTPクライアントのAbstractVerifier
で実装されたロジックです。それ以降のバージョン startHandshake()
を明示的に呼び出す 、 issue HTTPCLIENT-1346 のレポートに基づいています。
これは最終的に Sun.security. ssl.SSLSocketImpl.getSession()
の実装に由来するようです。これは、startHandshake(false)
(内部メソッド)を呼び出すときにスローされる可能性のあるIOException
sを、それ以上スローせずにキャッチします。これはバグである可能性がありますが、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();
ありがたいことに、実際にそのSSLSocket
を使おうとすると、これでも「javax.net.ssl.SSLHandshakeException: Sun.security.validator.ValidatorException: PKIX path building failed
」がスローされます(ピア証明書を取得せずにセッションを取得することで抜け穴はありません)。
これを修正する方法
信頼されていない証明書に関する他の問題と同様に、使用しているトラストストアに必要なトラストアンカー(つまり、検証しようとしているチェーンを発行したCA証明書、または場合によっては実際のサーバー)が含まれていることを確認する必要があります。例外的な場合の証明書)。
これを修正するには、CA証明書(または場合によってはサーバー証明書自体)をトラストストアにインポートする必要があります。あなたはこれを行うことができます:
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
を作成し、それをApacheHttpクライアントとの接続に使用することです。
このファクトリは、通常どおりSSLSocket
を作成する必要があります。そのソケットを返す前に、sslSocket.setEnabledProtocols(new String[] {"SSLv3"});
を使用して、TLSを無効にします。TLSを無効にすると、デフォルトで有効になります。
カスタムコンテキストを作成して、証明書が無効である理由をログに記録できるようにします。または、単にデバッグします。