259 votes

Accepter le certificat ssl auto-signé du serveur dans le client Java

Cela ressemble à une question standard, mais je n'ai pas trouvé de directives claires.

J'ai un code java qui essaie de se connecter à un serveur avec un certificat probablement auto-signé (ou expiré). Le code signale l'erreur suivante :

[HttpMethodDirector] I/O exception (javax.net.ssl.SSLHandshakeException) caught 
when processing request: sun.security.validator.ValidatorException: PKIX path 
building failed: sun.security.provider.certpath.SunCertPathBuilderException: 
unable to find valid certification path to requested target

Si je comprends bien, je dois utiliser keytool et dire à Java que c'est bon d'autoriser cette connexion.

Toutes les instructions pour résoudre ce problème supposent que je maîtrise parfaitement keytool, par exemple

générer une clé privée pour le serveur et l'importer dans le keystore

Y a-t-il quelqu'un qui pourrait poster des instructions détaillées ?

Je suis sous unix, donc bash script serait le mieux.

Je ne sais pas si c'est important, mais le code a été exécuté dans Jboss.

2 votes

Voir Comment accepter un certificat auto-signé avec une HttpsURLConnection Java ? . Évidemment, il serait préférable que vous puissiez faire en sorte que le site utilise un certificat valide.

2 votes

Merci pour le lien, je ne l'avais pas vu en cherchant. Mais les deux solutions proposées impliquent un code spécial pour envoyer une requête et j'utilise un code existant (amazon ws client for java). Respectivement, c'est leur site que je connecte et je ne peux pas résoudre ses problèmes de certificat.

5 votes

@MatthewFlaschen - "Évidemment, ce serait mieux si vous pouviez faire en sorte que le site utilise un cert valide..." - Un certificat auto-signé est un certificat valide si le client lui fait confiance. Beaucoup pensent que le fait de confier la confiance au cartel CA/Navigateur est un défaut de sécurité.

3voto

user834799 Points 309

Faites confiance à tous les certificats SSL :- Vous pouvez contourner SSL si vous voulez tester sur le serveur de test. Mais n'utilisez pas ce code pour la production.

public static class NukeSSLCerts {
protected static final String TAG = "NukeSSLCerts";

public static void nuke() {
    try {
        TrustManager[] trustAllCerts = new TrustManager[] { 
            new X509TrustManager() {
                public X509Certificate[] getAcceptedIssuers() {
                    X509Certificate[] myTrustedAnchors = new X509Certificate[0];  
                    return myTrustedAnchors;
                }

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

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

        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCerts, new SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
        HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
            @Override
            public boolean verify(String arg0, SSLSession arg1) {
                return true;
            }
        });
    } catch (Exception e) { 
    }
}

}

Veuillez appeler cette fonction dans la fonction onCreate() de l'activité ou dans votre classe d'application.

NukeSSLCerts.nuke();

Ceci peut être utilisé pour Volley dans Android.

0 votes

Je ne sais pas vraiment pourquoi vous avez été rejeté, à moins que le code ne fonctionne pas. Peut-être est-ce l'hypothèse qu'Android est impliqué d'une manière ou d'une autre alors que la question originale ne mentionnait pas Android.

3voto

user3132194 Points 46

Téléchargez votre certificat auto-signé à l'aide de votre navigateur à partir de la page cible et ajoutez-le à l'adresse suivante stockage par défaut avec mot de passe par défaut :

keytool -import -v -trustcacerts -file selfsigned.crt -alias myserver -keystore /etc/alternatives/jre/lib/security/cacerts -storepass changeit

Utiliser le fichier $JAVA_HOME/jre/lib/security/cacerts Mon exemple ici est celui d'Oracle linux 7.7.

2voto

EJP Points 113412

S'ils utilisent un certificat auto-signé, c'est à eux de prendre les mesures nécessaires pour rendre leur serveur utilisable. Plus précisément, cela signifie qu'ils doivent vous fournir leur certificat hors ligne de manière fiable. Demandez-lui donc de le faire. Vous l'importerez ensuite dans votre truststore en utilisant l'outil keytool comme décrit dans le guide de référence JSSE. Ne pensez même pas au TrustManager non sécurisé affiché ici.

EDIT Au profit de la dix-sept ( !) et de nombreux commentateurs ci-dessous, qui n'ont manifestement pas lu ce que j'ai écrit ici, voici ce que j'en pense pas une jérémiade contre les certificats auto-signés. Il n'y a rien de mal avec les certificats auto-signés lorsqu'elle est mise en œuvre correctement. Mais, la manière correcte de les mettre en œuvre est de faire délivrer le certificat de manière sécurisée via un processus hors ligne, plutôt que via le canal non authentifié qu'ils vont être utilisés pour authentifier. Cela n'est-il pas évident ? C'est en tout cas évident pour toutes les organisations conscientes de la sécurité pour lesquelles j'ai travaillé, des banques avec des milliers de filiales à mes propres entreprises. La "solution" de la base de code côté client qui consiste à faire confiance à tous les certificats, y compris les certificats auto-signés signés par n'importe qui, ou tout organisme arbitraire s'érigeant en autorité de certification. ipso facto pas sécurisé. C'est juste un jeu de sécurité. C'est inutile. Vous avez une conversation privée, inviolable, sans possibilité de réponse, sans possibilité d'injection avec... quelqu'un. N'importe qui. Un homme au milieu. Un imitateur. N'importe qui. Vous pouvez tout aussi bien utiliser du texte en clair.

3 votes

Ce n'est pas parce qu'un serveur a décidé d'utiliser https, que la personne avec le client n'a rien à faire de la sécurité pour ses propres besoins.

3 votes

Je suis surpris que cette réponse ait été autant descendue. J'aimerais comprendre un peu mieux pourquoi. Il semble que l'EJP suggère que vous ne devriez pas faire l'option 2 car elle permet une faille de sécurité majeure. Quelqu'un pourrait-il expliquer pourquoi (à part les paramètres de pré-déploiement) cette réponse est de mauvaise qualité ?

0 votes

@Gus Si le serveur a décidé d'utiliser HTTPS c'est parce que ils veulent une certaine sécurité, et ils sont en droit d'attendre du client qu'il ne la compromette pas.

2voto

David Ryan Points 41

J'ai eu le problème de passer une URL dans une bibliothèque qui appelait url.openConnection(); J'ai adapté la réponse de jon-daniel,

public class TrustHostUrlStreamHandler extends URLStreamHandler {

    private static final Logger LOG = LoggerFactory.getLogger(TrustHostUrlStreamHandler.class);

    @Override
    protected URLConnection openConnection(final URL url) throws IOException {

        final URLConnection urlConnection = new URL(url.getProtocol(), url.getHost(), url.getPort(), url.getFile()).openConnection();

        // adapated from
        // https://stackoverflow.com/questions/2893819/accept-servers-self-signed-ssl-certificate-in-java-client
        if (urlConnection instanceof HttpsURLConnection) {
            final HttpsURLConnection conHttps = (HttpsURLConnection) urlConnection;

            try {
                // Set up a Trust all manager
                final TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {

                    @Override
                    public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                        return null;
                    }

                    @Override
                    public void checkClientTrusted(final java.security.cert.X509Certificate[] certs, final String authType) {
                    }

                    @Override
                    public void checkServerTrusted(final java.security.cert.X509Certificate[] certs, final String authType) {
                    }
                } };

                // Get a new SSL context
                final SSLContext sc = SSLContext.getInstance("TLSv1.2");
                sc.init(null, trustAllCerts, new java.security.SecureRandom());
                // Set our connection to use this SSL context, with the "Trust all" manager in place.
                conHttps.setSSLSocketFactory(sc.getSocketFactory());
                // Also force it to trust all hosts
                final HostnameVerifier allHostsValid = new HostnameVerifier() {
                    @Override
                    public boolean verify(final String hostname, final SSLSession session) {
                        return true;
                    }
                };

                // and set the hostname verifier.
                conHttps.setHostnameVerifier(allHostsValid);

            } catch (final NoSuchAlgorithmException e) {
                LOG.warn("Failed to override URLConnection.", e);
            } catch (final KeyManagementException e) {
                LOG.warn("Failed to override URLConnection.", e);
            }

        } else {
            LOG.warn("Failed to override URLConnection. Incorrect type: {}", urlConnection.getClass().getName());
        }

        return urlConnection;
    }

}

En utilisant cette classe, il est possible de créer une nouvelle URL avec :

trustedUrl = new URL(new URL(originalUrl), "", new TrustHostUrlStreamHandler());
trustedUrl.openConnection();

Cela présente l'avantage d'être localisé et de ne pas remplacer le système par défaut. URL.openConnection .

1voto

La réponse acceptée est bonne, mais j'aimerais y ajouter quelque chose car j'utilisais IntelliJ sur Mac et je n'arrivais pas à le faire fonctionner en utilisant la fonction JAVA_HOME variable de chemin.

Il s'avère que Java Home était différent lors de l'exécution de l'application depuis IntelliJ.

Pour savoir exactement où elle se trouve, vous pouvez simplement faire System.getProperty("java.home") car c'est à partir de là que les certificats de confiance sont lus.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X