web-dev-qa-db-fra.com

Connexion url Java et HTTPS sans téléchargement du certificat

Ce code se connecte à un site HTTPS et je suppose que je ne vérifie pas le certificat. Mais pourquoi ne dois-je pas installer un certificat localement pour le site? Ne devrais-je pas installer un certificat localement et le charger pour ce programme ou est-il téléchargé sous le capot? Le trafic entre le client et le site distant est-il toujours crypté lors de la transmission?

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 //
42
Berlin Brown

La raison pour laquelle vous n'avez pas à charger un certificat localement est que vous avez explicitement choisi de ne pas vérifier le certificat, avec ce gestionnaire de confiance qui approuve tous les certificats.

Le trafic sera toujours chiffré, mais vous établissez une connexion avec des attaques Man-In-The-Middle: vous communiquez en secret avec une personne, vous ne savez pas si c'est le serveur que vous attendez ou un attaquant possible.

Si votre certificat de serveur provient d'une autorité de certification connue, faisant partie de l'ensemble par défaut de certificats d'autorité de certification fourni avec le JRE (généralement le fichier cacerts, voir le guide de référence JSSE), vous pouvez simplement utiliser le gestionnaire de confiance par défaut. mettre n'importe quoi ici.

Si vous avez un certificat spécifique (auto-signé ou de votre propre autorité de certification), vous pouvez utiliser le gestionnaire de confiance par défaut ou peut-être un initialisé avec un magasin de clés de confiance, mais vous devrez importer le certificat explicitement dans votre magasin de clés de confiance vérification), comme décrit dans cette réponse . Vous pouvez également être intéressé par cette réponse .

30
Bruno

Mais pourquoi ne dois-je pas installer un certificat localement pour le site?

Eh bien, le code que vous utilisez est explicitement conçu pour accepter le certificat sans effectuer aucune vérification. Ce n'est pas une bonne pratique ... mais si c'est ce que vous voulez faire, il est (évidemment) inutile d'installer un certificat que votre code ignore explicitement.

Ne devrais-je pas installer un certificat localement et le charger pour ce programme ou est-il téléchargé sous le capot? 

Non et non Voir au dessus.

Le trafic entre le client et le site distant est-il toujours crypté lors de la transmission?

Oui, ça l'est. Cependant, le problème est que, puisque vous lui avez dit de faire confiance au certificat du serveur sans faire aucune vérification, vous ne savez pas si vous parlez au vrai serveur, ou à un autre site qui est feignant soyez le vrai serveur. Que ce soit un problème dépend des circonstances.


Si nous utilisons le navigateur comme exemple, un navigateur ne demande généralement pas à l'utilisateur d'installer explicitement un certificat pour chaque site SSL visité. 

Le navigateur dispose d'un ensemble de certificats racine approuvés préinstallés. La plupart du temps, lorsque vous visitez un site "https", le navigateur peut vérifier que le certificat du site est (finalement, via la chaîne de certificats) sécurisé par l'un de ces certificats de confiance. Si le navigateur ne reconnaît pas le certificat au début de la chaîne en tant que certificat de confiance (ou si les certificats sont obsolètes ou autrement invalides/inappropriés), un avertissement s'affiche.

Java fonctionne de la même manière. Le fichier de clés de la machine virtuelle Java contient un ensemble de certificats de confiance. Le même processus est utilisé pour vérifier que le certificat est sécurisé par un certificat de confiance.

L'API client Java https prend-elle en charge un type de mécanisme permettant de télécharger automatiquement les informations de certificat?

Autoriser les applications à télécharger des certificats à des emplacements aléatoires et à les installer (de manière sécurisée) dans le magasin de clés du système constituerait un trou de sécurité.

12
Stephen C

Utilisez le dernier X509ExtendedTrustManager au lieu du X509Certificate comme conseillé ici: Java.security.cert.CertificateException: les certificats ne sont pas conformes aux contraintes de l'algorithme

    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

        }

    }
3
Tarik

Connexion url Java et HTTPS sans téléchargement du certificat

Si vous voulez vraiment éviter de télécharger le certificat du serveur, utilisez un protocole anonyme tel que Anonymous Diffie-Hellman (ADH). Le certificat du serveur n'est pas envoyé avec ADH et ses amis.

Vous sélectionnez un protocole anonyme avec setEnabledCipherSuites. Vous pouvez voir la liste des suites de chiffrement disponibles avec getEnabledCipherSuites.

En relation: c’est pourquoi vous devez appeler SSL_get_peer_certificate dans OpenSSL. Vous obtiendrez un X509_V_OK avec un protocole anonyme, et vous pourrez ainsi vérifier si un certificat a été utilisé dans l'échange.

Mais comme le signalent Bruno et Stephed C, c’est une mauvaise idée d’éviter les vérifications ou d’utiliser un protocole anonyme.


Une autre option consiste à utiliser TLS-PSK ou TLS-SRP. Ils n'ont pas besoin de certificats de serveur non plus. (Mais je ne pense pas que vous puissiez les utiliser).

Le problème, c'est que vous devez être pré-approvisionné dans le système car TLS-PSK est Pres-shared Secret et TLS-SRP est Secure Remote Password . L'authentification est mutuelle plutôt que serveur.

Dans ce cas, l'authentification mutuelle est fournie par une propriété selon laquelle les deux parties connaissent le secret partagé et parviennent au même secret préalable. ou un (ou les deux) ne pas et la configuration du canal échoue. Chaque partie prouve que la connaissance du secret est la partie "mutuelle".

Enfin, TLS-PSK ou TLS-SRP ne font pas de bêtises, telles que le mot de passe de l'utilisateur, comme dans une application Web utilisant HTTP (ou via HTTPS). C'est pourquoi j'ai dit chaque partie prouve la connaissance du secret ...

1
jww

Une solution Java simple, mais pas pure, consiste à passer de curl à partir de Java, ce qui vous donne un contrôle complet sur la manière dont la demande est effectuée. Si vous ne faites que cela pour quelque chose de simple, cela vous permet d'ignorer parfois les erreurs de certificat, en utilisant cette méthode. Cet exemple montre comment effectuer une requête sur un serveur sécurisé avec un certificat valide ou non valide, transmettre un cookie et obtenir le résultat à l'aide de curl à partir de 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);
    }
  }


}
1
Brad Parks

Si vous utilisez une passerelle de paiement pour frapper n'importe quelle URL juste pour envoyer un message, j'ai utilisé une vue Web en la suivant: Comment charger une URL https sans utiliser ssl dans la vue Web Android

et créez une vue Web dans votre activité sans visibilité. Ce que vous devez faire: chargez cette webview .. comme ceci: 

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

Facile.

Vous obtiendrez ceci: SSLTolerentWebViewClient à partir de ce lien: Comment charger une URL https sans utiliser ssl dans la vue Web Android

0
Amanpreet Singh