54

このコードは HTTPS サイトに接続し、証明書を検証していないと想定しています。しかし、サイトの証明書をローカルにインストールする必要がないのはなぜですか? 証明書をローカルにインストールしてこのプログラム用にロードする必要はありませんか、それとも裏でダウンロードされますか? クライアントからリモート サイトへのトラフィックは、送信時に暗号化されていますか?

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
import java.net.URLConnection;
import java.security.cert.X509Certificate;

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;

public class TestSSL {

    public static void main(String[] args) throws Exception {
        // 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
        final 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);

        URL url = new URL("https://www.google.com");
        URLConnection con = url.openConnection();
        final Reader reader = new InputStreamReader(con.getInputStream());
        final BufferedReader br = new BufferedReader(reader);        
        String line = "";
        while ((line = br.readLine()) != null) {
            System.out.println(line);
        }        
        br.close();
    } // End of main 
} // End of the class //
4

6 に答える 6

33

証明書をローカルにロードする必要がない理由は、すべての証明書を信頼するこの信頼マネージャーを使用して、証明書を検証しないことを明示的に選択したためです。

トラフィックは引き続き暗号化されますが、中間者攻撃への接続を開始します。誰かと密かに通信している、それが期待するサーバーなのか、攻撃者の可能性があるのか​​わからないだけです。

サーバー証明書が、JREにバンドルされているCA証明書のデフォルトバンドルの一部である既知のCAからのものである場合(通常はcacertsファイル、JSSEリファレンスガイドを参照)、デフォルトの信頼マネージャーを使用できます。設定する必要はありません。ここに何でも。

特定の証明書(自己署名または独自のCAから)がある場合は、デフォルトのトラストマネージャー、または特定のトラストストアで初期化されたものを使用できますが、証明書をトラストストアに明示的にインポートする必要があります(独立後検証)、この回答で説明されているように。この回答にも興味があるかもしれません。

于 2012-10-23T02:31:53.403 に答える
12

しかし、サイトの証明書をローカルにインストールする必要がないのはなぜですか?

使用しているコードは、何のチェックも行わずに証明書を受け入れるように明示的に設計されています。これは良い習慣ではありません...しかし、それがあなたのやりたいことなら、(明らかに)あなたのコードが明示的に無視している証明書をインストールする必要はありません。

証明書をローカルにインストールしてこのプログラム用にロードする必要はありませんか、それとも裏でダウンロードされますか?

いいえ、いいえ。上記を参照。

クライアントからリモート サイトへのトラフィックは、送信時に暗号化されていますか?

はい、そうです。ただし、問題は、チェックを行わずにサーバーの証明書を信頼するように指示したため、実際のサーバーと通信しているのか、または実際のサーバーのふりをしている他のサイトと通信しているのかがわからないことです。これが問題になるかどうかは、状況によって異なります。


例としてブラウザを使用した場合、通常、ブラウザは、訪問した各 SSL サイトの証明書を明示的にインストールするようユーザーに要求しません。

ブラウザーには、一連の信頼されたルート証明書が事前にインストールされています。ほとんどの場合、「https」サイトにアクセスすると、ブラウザーはサイトの証明書が (最終的には証明書チェーンを介して) これらの信頼できる証明書のいずれかによって保護されていることを確認できます。ブラウザがチェーンの最初にある証明書を信頼できる証明書として認識しない場合 (または、証明書が期限切れであるか、無効/不適切である場合)、警告が表示されます。

Java も同じように動作します。JVM のキーストアには信頼できる証明書のセットがあり、同じプロセスを使用して、証明書が信頼できる証明書によって保護されていることを確認します。

Java https クライアント API は、証明書情報を自動的にダウンロードする何らかのメカニズムをサポートしていますか?

いいえ。アプリケーションがランダムな場所から証明書をダウンロードし、それらをシステム キーストアに (信頼できるものとして) インストールできるようにすると、セキュリティ ホールになります。

于 2012-10-23T02:34:52.327 に答える
7

ここでアドバイスされているように、X509Certificate の代わりに最新の X509ExtendedTrustManager を使用してください: java.security.cert.CertificateException: 証明書がアルゴリズムの制約に準拠していません。

    package javaapplication8;

    import java.io.InputStream;
    import java.net.Socket;
    import java.net.URL;
    import java.net.URLConnection;
    import java.security.cert.CertificateException;
    import java.security.cert.X509Certificate;
    import javax.net.ssl.HostnameVerifier;
    import javax.net.ssl.HttpsURLConnection;
    import javax.net.ssl.SSLContext;
    import javax.net.ssl.SSLEngine;
    import javax.net.ssl.SSLSession;
    import javax.net.ssl.TrustManager;
    import javax.net.ssl.X509ExtendedTrustManager;

    /**
     *
     * @author hoshantm
     */
    public class JavaApplication8 {

        /**
         * @param args the command line arguments
         * @throws java.lang.Exception
         */
        public static void main(String[] args) throws Exception {
            /*
             *  fix for
             *    Exception in thread "main" javax.net.ssl.SSLHandshakeException:
             *       sun.security.validator.ValidatorException:
             *           PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException:
             *               unable to find valid certification path to requested target
             */
            TrustManager[] trustAllCerts = new TrustManager[]{
                new X509ExtendedTrustManager() {
                    @Override
                    public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                        return null;
                    }

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

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

                    @Override
                    public void checkClientTrusted(X509Certificate[] xcs, String string, Socket socket) throws CertificateException {

                    }

                    @Override
                    public void checkServerTrusted(X509Certificate[] xcs, String string, Socket socket) throws CertificateException {

                    }

                    @Override
                    public void checkClientTrusted(X509Certificate[] xcs, String string, SSLEngine ssle) throws CertificateException {

                    }

                    @Override
                    public void checkServerTrusted(X509Certificate[] xcs, String string, SSLEngine ssle) throws CertificateException {

                    }

                }
            };

            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() {
                @Override
                public boolean verify(String hostname, SSLSession session) {
                    return true;
                }
            };
            // Install the all-trusting host verifier
            HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
            /*
             * end of the fix
             */

            URL url = new URL("https://10.52.182.224/cgi-bin/dynamic/config/panel.bmp");
            URLConnection con = url.openConnection();
            //Reader reader = new ImageStreamReader(con.getInputStream());

            InputStream is = new URL(url.toString()).openStream();

            // Whatever you may want to do next

        }

    }
于 2016-10-04T11:32:10.437 に答える
1

単純ではあるが純粋な Java ソリューションではないのは、curlfrom Java にシェル アウトすることです。これにより、リクエストの実行方法を完全に制御できます。簡単なことのためにこれを行っているだけの場合は、この方法を使用して、証明書エラーを無視することができます。この例では、有効または無効な証明書を使用してセキュア サーバーに対して要求を行い、Cookie を渡し、curlfrom java.xml を使用して出力を取得する方法を示します。

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;

public class MyTestClass
{
  public static void main(String[] args) 
  {
    String url = "https://www.google.com";
    String sessionId = "faf419e0-45a5-47b3-96d1-8c62b2a3b558";

    // Curl options are:
    // -k: ignore certificate errors
    // -L: follow redirects
    // -s: non verbose
    // -H: add a http header

    String[] command = { "curl", "-k", "-L", "-s", "-H", "Cookie: MYSESSIONCOOKIENAME=" + sessionId + ";", "-H", "Accept:*/*", url };
    String output = executeShellCmd(command, "/tmp", true, true);
    System.out.println(output);
  }

  public String executeShellCmd(String[] command, String workingFolder, boolean wantsOutput, boolean wantsErrors)
  {
    try
    {
      ProcessBuilder pb = new ProcessBuilder(command);
      File wf = new File(workingFolder);
      pb.directory(wf);

      Process proc = pb.start();
      BufferedReader stdInput = new BufferedReader(new InputStreamReader(proc.getInputStream()));
      BufferedReader stdError = new BufferedReader(new InputStreamReader(proc.getErrorStream()));

      StringBuffer sb = new StringBuffer();
      String newLine = System.getProperty("line.separator");
      String s;

      // read stdout from the command
      if (wantsOutput)
      {
        while ((s = stdInput.readLine()) != null)
        {
          sb.append(s);
          sb.append(newLine);
        }
      }

      // read any errors from the attempted command
      if (wantsErrors)
      {
        while ((s = stdError.readLine()) != null)
        {
          sb.append(s);
          sb.append(newLine);
        }
      }

      String result = sb.toString();

      return result;
    }
    catch (IOException e)
    {
      throw new RuntimeException("Problem occurred:", e);
    }
  }


}
于 2016-08-11T19:52:17.843 に答える