web-dev-qa-db-ja.com

Apache HTTPClient SSLPeerUnverifiedException

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でエラーを無視するように強制できると思いますが、セキュリティホールを開きたくないので、根本原因を修正したいと思います。

何か案は?

14
Oliver Kohll

[〜#〜] 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)(内部メソッド)を呼び出すときにスローされる可能性のあるIOExceptionsを、それ以上スローせずにキャッチします。これはバグである可能性がありますが、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証明書(または場合によってはサーバー証明書自体)をトラストストアにインポートする必要があります。あなたはこれを行うことができます:

  • 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を作成し、それをApacheHttpクライアントとの接続に使用することです。

このファクトリは、通常どおりSSLSocketを作成する必要があります。そのソケットを返す前に、sslSocket.setEnabledProtocols(new String[] {"SSLv3"});を使用して、TLSを無効にします。TLSを無効にすると、デフォルトで有効になります。

21
Bruno

カスタムコンテキストを作成して、証明書が無効である理由をログに記録できるようにします。または、単にデバッグします。

0
David Roussel