14

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

何か案は?

4

2 に答える 2

22

編集この回答はずっと前に受け入れられ、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.SSLSocketFactoryApache ではなく を使用)。

例 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 を、それ以上スローせずにキャッチする の実装に最終的に由来するようです。これはバグである可能性がありますが、これはセキュリティに大きな影響を与えるべきではありません。IOExceptionstartHandshake(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 はデフォルトで有効になります。

于 2012-08-23T16:40:52.317 に答える
0

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

于 2012-08-23T16:27:50.843 に答える