web-dev-qa-db-ja.com

Jsoupを使用してHTTPS経由で接続する方法

HTTPで正常に動作していますが、HTTPSソースを使用しようとすると、次の例外がスローされます。

10-12 13:22:11.169: WARN/System.err(332): javax.net.ssl.SSLHandshakeException: Java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
10-12 13:22:11.179: WARN/System.err(332):     at org.Apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.Java:477)
10-12 13:22:11.179: WARN/System.err(332):     at org.Apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.Java:328)
10-12 13:22:11.179: WARN/System.err(332):     at org.Apache.harmony.luni.internal.net.www.protocol.http.HttpConnection.setupSecureSocket(HttpConnection.Java:185)
10-12 13:22:11.179: WARN/System.err(332):     at org.Apache.harmony.luni.internal.net.www.protocol.https.HttpsURLConnectionImpl$HttpsEngine.makeSslConnection(HttpsURLConnectionImpl.Java:433)
10-12 13:22:11.189: WARN/System.err(332):     at org.Apache.harmony.luni.internal.net.www.protocol.https.HttpsURLConnectionImpl$HttpsEngine.makeConnection(HttpsURLConnectionImpl.Java:378)
10-12 13:22:11.189: WARN/System.err(332):     at org.Apache.harmony.luni.internal.net.www.protocol.http.HttpURLConnectionImpl.connect(HttpURLConnectionImpl.Java:205)
10-12 13:22:11.189: WARN/System.err(332):     at org.Apache.harmony.luni.internal.net.www.protocol.https.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.Java:152)
10-12 13:22:11.189: WARN/System.err(332):     at org.jsoup.helper.HttpConnection$Response.execute(HttpConnection.Java:377)
10-12 13:22:11.189: WARN/System.err(332):     at org.jsoup.helper.HttpConnection$Response.execute(HttpConnection.Java:364)
10-12 13:22:11.189: WARN/System.err(332):     at org.jsoup.helper.HttpConnection.execute(HttpConnection.Java:143)

関連するコードは次のとおりです。

try {
    doc = Jsoup.connect("https url here").get();
} catch (IOException e) {
    Log.e("sys","coudnt get the html");
    e.printStackTrace();
}
23
jfisk

正しい方法で実行したい場合、および/または1つのサイトのみを処理する必要がある場合は、基本的には問題のWebサイトのSSL証明書を取得し、Javaキーストアにインポートする必要があります。 。これにより、Jsoup(またはJava.net.URLConnection)を使用する前にSSLトラストストアとして設定したJKSファイルが生成されます。

Webブラウザーのストアから証明書を取得できます。 Firefoxを使用しているとします。

  1. Firefoxを使用して問題のWebサイトにアクセスします。これは、あなたのケースでは https://web2.uconn.edu/driver/old/timepoints.php?stopid=1
  2. アドレスバーの左側に「uconn.edu」が青色で表示されます(これは有効なSSL証明書を示します)
  3. 詳細については、それをクリックして、詳細情報ボタンをクリックしてください。
  4. 表示されるセキュリティダイアログで、証明書の表示ボタンをクリックします。
  5. 表示される証明書パネルで、詳細タブに移動します。
  6. 証明書階層の最も深い項目(この場合は "web2.uconn.edu")をクリックし、最後にExportボタンをクリックします。

これでweb2.uconn.edu.crtファイルができました。

次に、コマンドプロンプトを開き、keytoolコマンドを使用してJavaキーストアにインポートします(JREの一部です)。

keytool -import -v -file /path/to/web2.uconn.edu.crt -keystore /path/to/web2.uconn.edu.jks -storepass drowssap

-fileは、ダウンロードした.crtファイルの場所を指す必要があります。 -keystoreは、生成された.jksファイル(SSLトラストストアとして設定するファイル)の場所を指す必要があります。 -storepassは必須です。6文字以上であれば、任意のパスワードを入力できます。

これで、web2.uconn.edu.jksファイルができました。次のように、接続する前に最終的にSSLトラストストアとして設定できます。

System.setProperty("javax.net.ssl.trustStore", "/path/to/web2.uconn.edu.jks");
Document document = Jsoup.connect("https://web2.uconn.edu/driver/old/timepoints.php?stopid=10").get();
// ...

まったく異なる代替手段として、特に複数のサイトを処理する必要がある場合(つまり、World Wide Webクローラーを作成している場合)、すべてのSSL証明書を盲目的に信頼するようにJsoup(基本的にはJava.net.URLConnection)に指示することもできます。この回答の最下部にあるセクション「信頼されていない、または誤って構成されたHTTPSサイトに対処する」も参照してください。 Java.net.URLConnectionを使用してHTTPリクエストを起動および処理する

55
BalusC

私の場合、接続に.validateTLSCertificates(false)を追加するだけで済みました

Document doc  = Jsoup.connect(httpsURLAsString)
            .timeout(60000).validateTLSCertificates(false).get();

読み取りタイムアウトも増やす必要がありましたが、これは無関係です

12
johnmerm

ここでの回答と検索のリンクされた質問に出くわし、2つの情報を追加したいと思います。受け入れられた回答は私の非常に類似したシナリオに適合しないためですが、その場合にも当てはまる追加の解決策があります(証明書およびホスト名がテストシステムと一致しません)。

  1. そのような機能を追加するgithubリクエストがあります。したがって、おそらくすぐに問題は解決されます: https://github.com/jhy/jsoup/pull/34 編集:Githubリクエストが解決され、証明書の検証を無効にする方法は次のとおりです:validateTLSCertificates(boolean validate )
  2. http://www.nakov.com/blog/2009/07/16/disable-certificate-validation-in-Java-ssl-connections/ に基づいて動作するように見えるソリューションを見つけました(少なくとも、jsoup 1.7.3がMavenタスクの一部として呼び出される私のシナリオでは)。最初のJsoup.connect()の前に呼び出すメソッドdisableSSLCertCheck()でラップしました。

この方法を使用する前に、そこで行うことを確実に理解しておく必要があります。SSL証明書をチェックしないのは本当に愚かなことです。一般に受け入れられているCAによって署名されているサーバーには、常に正しいSSL証明書を使用してください。一般に受け入れられているCAを購入する余裕がない場合でも、上記の@BalusCで受け入れられた回答を使用して正しいSSL証明書を使用してください。正しいSSL証明書を構成できない場合(本番環境では当てはまりません)、次の方法が機能します。

    private void disableSSLCertCheck() throws NoSuchAlgorithmException, KeyManagementException {
    // Create a trust manager that does not validate certificate chains
    TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager() {
            public Java.security.cert.X509Certificate[] getAcceptedIssuers() {
                return null;
            }
            public void checkClientTrusted(X509Certificate[] certs, String authType) {
            }
            public void checkServerTrusted(X509Certificate[] certs, String authType) {
            }
        }
    };

    // Install the all-trusting trust manager
    SSLContext sc = SSLContext.getInstance("SSL");
    sc.init(null, trustAllCerts, new Java.security.SecureRandom());
    HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

    // Create all-trusting Host name verifier
    HostnameVerifier allHostsValid = new HostnameVerifier() {
        public boolean verify(String hostname, SSLSession session) {
            return true;
        }
    };

    // Install the all-trusting Host verifier
    HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
    }
9
NextThursday

そのソリューション以来:

Document doc  = Jsoup.connect(httpsURLAsString)
            .timeout(60000).validateTLSCertificates(false).get();

非推奨の警告が表示され、間もなく削除されます。ここでは代替オプション(基本的には answer で提案されているのと同じアイデア)を使用しますが、特定のJSoup接続での証明書の警告を抑制します。

Kotlin


val document = Jsoup.connect("url")
        .sslSocketFactory(socketFactory())
        .get()


private fun socketFactory(): SSLSocketFactory {
    val trustAllCerts = arrayOf<TrustManager>(object : X509TrustManager {
        @Throws(CertificateException::class)
        override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String) {
        }

        @Throws(CertificateException::class)
        override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String) {
        }

        override fun getAcceptedIssuers(): Array<X509Certificate> {
            return arrayOf()
        }
    })

    try {
        val sslContext = SSLContext.getInstance("TLS")
        sslContext.init(null, trustAllCerts, Java.security.SecureRandom())
        return sslContext.socketFactory
    } catch (e: Exception) {
        when (e) {
            is RuntimeException, is KeyManagementException -> {
                throw RuntimeException("Failed to create a SSL socket factory", e)
            }
            else -> throw e
        }
    }
}

Java



 Document document = Jsoup.connect("url")
        .sslSocketFactory(socketFactory())
        .get();


  private SSLSocketFactory socketFactory() {
    TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
      public Java.security.cert.X509Certificate[] getAcceptedIssuers() {
        return null;
      }

      public void checkClientTrusted(X509Certificate[] certs, String authType) {
      }

      public void checkServerTrusted(X509Certificate[] certs, String authType) {
      }
    }};

    try {
      SSLContext sslContext = SSLContext.getInstance("TLS");
      sslContext.init(null, trustAllCerts, new Java.security.SecureRandom());
      return sslContext.getSocketFactory();
    } catch (NoSuchAlgorithmException | KeyManagementException e) {
      throw new RuntimeException("Failed to create a SSL socket factory", e);
    }
  }

NB。前述のように、証明書を無視することは良い考えではありません。

4

私は同じ問題を抱えていましたが、怠惰なルートを取りました-証明書を無視してとにかく続行するようにアプリに指示してください。

私はここからコードを取得しました: JavaでローカルHTTPS URLを使用するにはどうすればよいですか?

これを機能させるには、これらのクラスをインポートする必要があります。

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

接続を確立しようとする前にそのメソッドをどこかで実行するだけで、証明書を信頼します。もちろん、実際に証明書が本物であることを確認したい場合は役に立ちませんが、自分の内部Webサイトなどを監視するのに適しています。

3
RobinCJ

この問題は開発環境でのみ発生しました。それを解決する解決策は、VMへのSSLを無視するいくつかのフラグを追加することでした:

-Ddeployment.security.TLSv1.1=false 
-Ddeployment.security.TLSv1.2=false
0
pawelini1

私はこの分野の専門家ではありませんが、Java.net APIを使用してHTTPS経由でWebサイトに接続しようとしたときに、同様の例外に遭遇しました。 HTTPSを使用してサイトにアクセスすると、ブラウザーはSSL証明書に関して多くの作業を行います。ただし、手動で(HTTP要求を使用して)サイトに接続している場合でも、すべての作業を実行する必要があります。今、この作業の正確な内容はわかりませんが、証明書をダウンロードし、Javaがそれらを見つけることができる場所に置くことと関係があります。うまくいけば、あなたを正しい方向に向けるリンクがあります方向。

http://confluence.atlassian.com/display/JIRA/Connecting+to+SSL+services

0
jeff

Jsoupで同じ問題に直面していましたが、接続してhttpsのURLのドキュメントを取得できませんでしたが、JDKのバージョンを1.7から1.8に変更すると、問題は解決しました。

それはあなたを助けるかもしれません:)

0
ramkishorbajpai