web-dev-qa-db-fra.com

Certificat importé dans le magasin de clés Java, JVM ignore le nouveau cert.

J'essaie de faire en sorte qu'une application s'exécutant sur Tomcat 6 se connecte à un serveur LDAP via SSL.

J'ai importé le certificat du serveur dans le magasin de clés en utilisant:

C:\Program Files\Java\jdk1.6.0_32\jre\lib\security>keytool -importcert -trustcacerts -file mycert -alias ca_alias -keystore "c:\Program Files\Java\jdk1.6.0_32\jre\lib\security\cacerts"

Lorsque je lance Tomcat avec le débogage SSL activé, selon les journaux, Tomcat utilise le fichier de certificat correct:

trustStore is: C:\Program Files\Java\jdk1.6.0_32\jre\lib\security\cacerts

Cependant, Tomcat n'ajoute pas le certificat que je viens d'importer - tous les autres certificats du fichier cacerts sont imprimés dans le journal - et la connexion échoue:

handling exception: 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

Redémarrer Tomcat n'aide pas. J'ai vérifié avec la commande keytool -list que le nouveau certificat existe bel et bien sur le fichier.

Pourquoi Tomcat continue à ignorer mon nouveau cert?

MODIFIER:

Il semble que le problème soit dû à Windows 7 VirtualStore. Keytool a créé une nouvelle copie du fichier cacert et Tomcat a utilisé le fichier d'origine.

18
tputkonen

La JVM doit être redémarrée après l'importation des certificats dans le magasin de clés.

16
SSLKida

Vérifiez s'il existe une clé avec les mêmes informations CN mais un alias différent.

J'ai déjà eu des problèmes similaires lorsque j'ai essayé d'importer une version plus récente d'un certificat mais que j'ai laissé l'ancienne version dans le magasin de clés. Mes programmes Java cherchaient simplement la première clé CN correspondante dans le magasin de clés (l'ancien système ayant expiré) et essayaient de l'utiliser, même s'il y en avait une plus récente correspondant au CN.

Assurez-vous également que le certificat racine d'authentification (et le certificat intermédiaire, le cas échéant) existe dans le magasin de clés. Si vous vous authentifiez auprès de l'un des principaux fournisseurs de sécurité, tels que Verisign ou Globalsign, ils vous fourniront généralement les certificats racine et intermédiaire. Si ces certificats existent déjà dans le magasin de clés, assurez-vous qu'ils sont toujours valides. Vous devez disposer de tous les certificats de votre certificat personnel tout au long de la chaîne d'authentification jusqu'à la racine, existant dans votre magasin de clés, afin qu'il sache comment valider vos informations d'identification.

5
wattostudios

Ce que vous avez décrit correspond exactement à ce que j’ai eu lors de l’utilisation de cmd.exe et d’un utilisateur normal bien que membre d’un groupe administratif sur un serveur Windows. Vous devez démarrer cmd.exe en mode administration pour appliquer les modifications dans les fichiers cacerts. Au moins sur les systèmes d'exploitation Win2k8. 

Si vous ne le faites pas, les carets vous montreront dans la vue keytool.exe -list les nouveaux certificats ajoutés mais Tomcat ne les verra pas. Je ne sais pas pourquoi. Mais lorsque vous l'ajoutez avec cmd.exe, démarré en tant qu'administrateur, Tomcat convient parfaitement aux certificats nouvellement ajoutés.

Vous pouvez également utiliser Djavax.net.debug="ssl,handshake" pour voir ce que Tomcat lit à partir du fichier cacerts.

2
jturunen

Dans mon cas, j'ai examiné tellement de choses avant de comprendre ce qui n'allait pas ... J'ai ajouté le certificat à différents magasins de clés, j'ai ajouté tous les certificats de la chaîne (ce qui est inutile en fait), j'ai téléchargé à nouveau le certificat pour ma santé personnelle. et vérifié le numéro de série, et même inspecté le CERT téléchargé pour s'assurer qu'il avait toutes les informations correctes.

J'ai fini par écrire une application cliente vérifiant TLS afin de déboguer le problème. Non seulement le serveur distant auquel je me connectais ne prenait en charge que TLS 1.2 (désactivé par défaut dans ma version de Java 7), mais il ne prenait en charge aucun des chiffrements activés sur mon client. Il s'avère que Java 7 n’a que moins de la moitié des chiffrements pris en charge activés, nombre d’entre eux étant vraiment peu sûrs et que certains des plus sécurisés ont été désactivés.

Après quelques vérifications, j'ai dressé la liste suivante des chiffrements sécurisés pris en charge par TLS 1.2:

new String[] {
    "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
    "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
    "TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256",
    "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256",
    "TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256",
    "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
    "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
    "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
    "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
    "TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384",
    "TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256",
    "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
    "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
    "TLS_DHE_PSK_WITH_AES_256_GCM_SHA384",
    "TLS_DHE_PSK_WITH_AES_128_GCM_SHA256",
    "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
    "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
    "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
    "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
    "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
    "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
    "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
    "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
    "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",
    "TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
    "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256",
    "TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
    "TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256",
    "TLS_DHE_RSA_WITH_AES_256_CCM",
    "TLS_DHE_RSA_WITH_AES_128_CCM",
    "TLS_DHE_PSK_WITH_AES_256_CCM",
    "TLS_DHE_PSK_WITH_AES_128_CCM",
    "TLS_CHACHA20_POLY1305_SHA256",
    "TLS_AES_256_GCM_SHA384",
    "TLS_AES_128_GCM_SHA256",
    "TLS_AES_128_CCM_SHA256"
}

S'il y a des experts en crypto autour, n'hésitez pas à mettre à jour cette liste. J'ai utilisé Qualys SSL Labs , this Information Security SE answer , et IANA comme source.

Pour ceux qui veulent un échantillon du code source que j'ai utilisé, voir ci-dessous. J'utilisais Apache Commons HttpClient 3.0, vous aurez donc probablement besoin de télécharger les fichiers binaires suivants:

TLS12SocketFactory.Java

import Java.io.*;
import Java.net.*;
import Java.util.*;
import org.Apache.commons.httpclient.params.HttpConnectionParams;
import org.Apache.commons.httpclient.protocol.*;
import org.Apache.commons.lang.StringUtils;

public class TLS12SocketFactory implements SecureProtocolSocketFactory {

        private final SecureProtocolSocketFactory base;
        public TLS12SocketFactory()
        {
            this.base = (SecureProtocolSocketFactory)Protocol.getProtocol("https").getSocketFactory();
        }
        private Socket acceptOnlyTLS12(Socket socket)
        {
            if(socket instanceof javax.net.ssl.SSLSocket) {
                final javax.net.ssl.SSLSocket s = (javax.net.ssl.SSLSocket)socket;

                // Set TLS 1.2
                s.setEnabledProtocols(new String[]{ "TLSv1.2" });

                // Using recommended ciphers from https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#table-tls-parameters-4
                List<String> recommended = new ArrayList(Arrays.asList(new String[]{ "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", "TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256", "TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256", "TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256", "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", "TLS_DHE_PSK_WITH_AES_256_GCM_SHA384", "TLS_DHE_PSK_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256", "TLS_DHE_RSA_WITH_AES_256_CBC_SHA", "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256", "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256", "TLS_DHE_RSA_WITH_AES_256_CCM", "TLS_DHE_RSA_WITH_AES_128_CCM", "TLS_DHE_PSK_WITH_AES_256_CCM", "TLS_DHE_PSK_WITH_AES_128_CCM", "TLS_CHACHA20_POLY1305_SHA256", "TLS_AES_256_GCM_SHA384", "TLS_AES_128_GCM_SHA256", "TLS_AES_128_CCM_SHA256" }));
                recommended.retainAll(Arrays.asList(s.getSupportedCipherSuites()));
                if(recommended.size() == 0) {
                    System.err.println("No supported modern ciphers. Update crypto policy or install JCE Unlimited Strength Jurisdiction Policy files." + System.lineSeparator());
                } else if(recommended.size() < 3) {
                    System.out.println("Few supported modern ciphers. It's recommended to update crypto policy or install JCE Unlimited Strength Jurisdiction Policy files." + System.lineSeparator());
                }
                s.setEnabledCipherSuites(recommended.toArray(new String[0]));

                // Log matched cipher and cert
                s.addHandshakeCompletedListener(new javax.net.ssl.HandshakeCompletedListener() {
                    @Override
                    public void handshakeCompleted(javax.net.ssl.HandshakeCompletedEvent hce) {
                        String print = s.getInetAddress().getHostName() + System.lineSeparator() + hce.getCipherSuite() + System.lineSeparator();
                        try {
                            for(Java.security.cert.Certificate cert : hce.getPeerCertificates()) {
                                List<String> certStrings = Arrays.asList(cert.toString().split("\r?\n"));
                                for(int line = 0; line < certStrings.size(); line++) {
                                    if(certStrings.get(line).startsWith("Certificate Extensions:")) {
                                        print += System.lineSeparator() + StringUtils.join(certStrings.subList(2, line-1), System.lineSeparator()) + System.lineSeparator();
                                        break;
                                    }
                                }
                            }
                        } catch (javax.net.ssl.SSLPeerUnverifiedException ex) {
                            print += "Non-certificate based cipher used" + System.lineSeparator();
                        }
                        System.out.println(print);
                    }
                });
            }
            return socket;
        }

        @Override
        public Socket createSocket(String Host, int port) throws IOException
        {
            return acceptOnlyTLS12(base.createSocket(Host, port));
        }
        @Override
        public Socket createSocket(String Host, int port, InetAddress localAddress, int localPort) throws IOException
        {
            return acceptOnlyTLS12(base.createSocket(Host, port, localAddress, localPort));
        }
        @Override
        public Socket createSocket(String Host, int port, InetAddress localAddress, int localPort, HttpConnectionParams params) throws IOException
        {
            return acceptOnlyTLS12(base.createSocket(Host, port, localAddress, localPort, params));
        }
        @Override
        public Socket createSocket(Socket socket, String Host, int port, boolean autoClose) throws IOException
        {
            return acceptOnlyTLS12(base.createSocket(socket, Host, port, autoClose));
        }
}

Main.Java

import Java.io.*;
import Java.security.*;
import Java.security.cert.*;
import Java.util.*;
import org.Apache.commons.httpclient.protocol.Protocol;
import org.Apache.commons.httpclient.*;
import org.Apache.commons.httpclient.cookie.CookiePolicy;
import org.Apache.commons.httpclient.methods.*;
import org.Apache.commons.httpclient.params.HttpClientParams;

public class Main {
    public static void main(String[] args) {
        List<Java.net.URI> locations = new ArrayList<>();
        for(String arg : args) {
            Java.net.URI location = Java.net.URI.create(arg);
            if(location.isAbsolute() && location.getScheme().equals("https")) {
                locations.add(location);
            } else {
                System.out.println("Skipping invalid URL: " + arg);
            }
        }
        System.out.println("Connecting to URL's");
        System.out.println();
        System.out.println("-------------------------");

        TLS12SocketFactory tls12factory = new TLS12SocketFactory();
        Protocol.registerProtocol("httpss", new Protocol("httpss", tls12factory, 443));
        for(Java.net.URI location : locations) {
            System.out.println();
            try {
                // Form request
                String tls12url = location.toString().replaceFirst("^https:", "httpss:");
                HttpMethod method = new HeadMethod(tls12url);

                // Send request
                HttpClientParams params = new HttpClientParams();
                params.setParameter(HttpClientParams.COOKIE_POLICY, CookiePolicy.IGNORE_COOKIES);
                new HttpClient(params).executeMethod(method);
                method.setFollowRedirects(true);

                // Print response
                System.out.println(location.toString());
                System.out.println(method.getStatusLine().toString());
            } catch (javax.net.ssl.SSLHandshakeException ex) {
                System.out.println("There was an error making a secure connection to " + location.getHost());
                ex.printStackTrace(System.out);
            } catch (HttpException ex) {
                System.out.println("There was an error with the external webpage");
                ex.printStackTrace(System.out);
            } catch (Exception ex) {
                System.out.println("Could not complete request");
                ex.printStackTrace(System.out);
            }
        }
        System.out.println();
        System.out.println("-------------------------");
        System.out.println();
        try {
            // Load supported SSL Ciphers
            System.out.println("Supported ciphers");
            System.out.println();
            System.out.println("-------------------------");
            System.out.println();
            javax.net.ssl.SSLSocket socket = (javax.net.ssl.SSLSocket)tls12factory.createSocket("www.google.com", 443);
            for(String cipher : socket.getSupportedCipherSuites()) {
                System.out.println(cipher);
            }
            System.out.println();
            System.out.println("-------------------------");
            System.out.println();

            // Load enabled SSL Ciphers
            System.out.println("Enabled ciphers");
            System.out.println();
            System.out.println("-------------------------");
            System.out.println();
            for(String cipher : socket.getEnabledCipherSuites()) {
                System.out.println(cipher);
            }
            System.out.println();
            System.out.println("-------------------------");
            System.out.println();

            // Load the JDK's cacerts keystore file
            String filename = System.getProperty("Java.home") + "/lib/security/cacerts".replace('/', File.separatorChar);
            System.out.println("Loading keystore");
            System.out.println(filename);
            System.out.println();
            System.out.println("-------------------------");
            System.out.println();
            FileInputStream is = new FileInputStream(filename);
            KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
            String password = "changeit";
            keystore.load(is, password.toCharArray());

            // This class retrieves the most-trusted CAs from the keystore
            PKIXParameters params = new PKIXParameters(keystore);

            // Get the set of trust anchors, which contain the most-trusted CA certificates
            for (TrustAnchor ta : params.getTrustAnchors()) {
                // Print certificate
                System.out.println(ta.getTrustedCert());
            }
        } catch (CertificateException | KeyStoreException | NoSuchAlgorithmException | InvalidAlgorithmParameterException | IOException ex) {
            System.out.println("Could not load keystore");
            ex.printStackTrace(System.out);
        }
    }
}
0
Pluto