Подтвердить что ты не робот

Java-соединение и HTTPS-соединение без скачивания сертификата

Этот код подключается к сайту 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 //
4b9b3361

Ответ 1

Причина, по которой вам не нужно загружать сертификат локально, заключается в том, что вы явно не проверяли сертификат, и этот доверительный менеджер доверяет всем сертификатам.

Трафик по-прежнему будет зашифрован, но вы откроете соединение с атак Man-In-The-Middle: вы тайно общаетесь с кем-то, вы просто не уверены, будет ли он ожидаемым сервером, или возможный злоумышленник.

Если ваш сертификат сервера поступает из известного ЦС, часть набора сертификатов ЦС по умолчанию, поставляемого вместе с JRE (обычно cacerts, см. в Справочном руководстве JSSE), вы можете просто использовать диспетчер доверия по умолчанию, вы не нужно ничего устанавливать здесь.

Если у вас есть конкретный сертификат (самозаверяющий или из собственного CA), вы можете использовать диспетчер доверия по умолчанию или, возможно, один, инициализированный с помощью определенного магазина доверия, но вам придется импортировать сертификат явно в хранилище доверия (после независимой проверки), как описано в этом ответе. Вы также можете быть заинтересованы в этом ответе.

Ответ 2

Но почему мне не нужно устанавливать сертификат локально для сайта?

Ну, код, который вы используете, явно предназначен для принятия сертификата без каких-либо проверок. Это не очень хорошая практика... но если это то, что вы хотите сделать, то (очевидно) нет необходимости устанавливать сертификат, явно игнорирующий ваш код.

Должен ли я установить локальный сертификат и загрузить его для этой программы или загрузить его за крышки?

Нет, и нет. См. Выше.

Является ли трафик между клиентом на удаленном сайте все еще зашифрованным при передаче?

Да, это так. Однако проблема заключается в том, что, поскольку вы сказали ей доверять сертификату сервера без каких-либо проверок, вы не знаете, разговариваете ли вы с реальным сервером или каким-либо другим сайтом, который притворяется реальным сервером. Является ли это проблемой, зависит от обстоятельств.


Если мы использовали браузер в качестве примера, обычно браузер не запрашивает у пользователя явно устанавливать сертификат для каждого посещенного сайта ssl.

В браузере установлен набор доверенных корневых сертификатов. В большинстве случаев, когда вы посещаете сайт "https", браузер может убедиться, что сертификат сайта (в конечном счете, через цепочку сертификатов) защищен одним из этих доверенных сертификатов. Если браузер не распознает сертификат в начале цепочки как доверенный сертификат (или если сертификаты устарели или иным образом недействительны/неуместны), тогда он выведет предупреждение.

Java работает одинаково. В хранилище ключей JVM имеется набор доверенных сертификатов, и тот же процесс используется для проверки того, что сертификат защищен доверенным сертификатом.

Поддерживает ли клиент java https api некоторый тип механизма для автоматической загрузки информации сертификата?

Нет. Разрешить приложениям загружать сертификаты из случайных мест и устанавливать их (как надежные) в хранилище ключей системы - это дыра в безопасности.

Ответ 3

Соединение с URL-адресом Java и HTTPS без загрузки сертификата

Если вы действительно хотите избежать загрузки сертификата сервера, используйте анонимный протокол, например Anonymous Diffie-Hellman (ADH). Сертификат сервера не отправляется с ADH и друзьями.

Вы выбираете анонимный протокол с setEnabledCipherSuites. Вы можете увидеть список доступных наборов шифров с помощью getEnabledCipherSuites.

Связано: почему вы должны называть SSL_get_peer_certificate в OpenSSL. Вы получите X509_V_OK с анонимным протоколом, и как вы проверяете, был ли сертификат использован в обмене.

Но, как заявили Бруно и Стивен С, его плохая идея избежать проверок или использовать анонимный протокол.


Другой вариант - использовать TLS-PSK или TLS-SRP. Они также не требуют сертификатов сервера. (Но я не думаю, что вы можете их использовать).

Врезь, вам необходимо предварительно предусмотреть в системе, потому что TLS-PSK является разделяемым секретом, а TLS-SRP - безопасным удаленным паролем. Аутентификация является взаимной, а не только сервером.

В этом случае взаимная аутентификация обеспечивается тем свойством, что обе стороны знают общий секрет и приходят к такому же первому тайну; или один (или оба), и настройка канала не выполняется. Каждая сторона доказывает, что секрет - это "взаимная" часть.

Наконец, TLS-PSK или TLS-SRP не делают ничего глупого, например, кашляют пароль пользователя, как в веб-приложении, используя HTTP (или через HTTPS). Вот почему я сказал, что каждая сторона доказывает знание тайны...

Ответ 4

Простое, но не чисто java-решение состоит в том, чтобы выложить на curl из java, что дает вам полный контроль над тем, как выполняется запрос. Если вы просто делаете это для чего-то простого, это позволяет вам иногда игнорировать ошибки сертификата, используя этот метод. В этом примере показано, как сделать запрос на безопасный сервер с допустимым или недопустимым сертификатом, передать в файл cookie и получить результат с помощью curl из java.

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);
    }
  }


}

Ответ 5

Используйте последний X509ExtendedTrustManager вместо X509Certificate, как указано здесь: 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

        }

    }

Ответ 6

Если вы используете какой-либо платежный шлюз для доступа к любому URL-адресу, просто чтобы отправить сообщение, я использовал веб-просмотр, следуя ему: Как загрузить https-url без использования ssl в веб-обозревателе Android

и сделайте веб-просмотр в своей деятельности с исчезновением видимости. Что вам нужно сделать: просто загрузите этот веб-просмотр.. вот так:

 webViewForSms.setWebViewClient(new SSLTolerentWebViewClient());
                webViewForSms.loadUrl(" https://bulksms.com/" +
                        "?username=test&[email protected]&messageType=text&mobile="+
                        mobileEditText.getText().toString()+"&senderId=ATZEHC&message=Your%20OTP%20for%20A2Z%20registration%20is%20124");

Легко.

Вы получите это: SSLTolerentWebViewClient по этой ссылке: Как загрузить https-url без использования ssl в веб-обозревателе Android