web-dev-qa-db-fra.com

Utilisation d’un fichier de clés de confiance personnalisé dans Java ainsi que celui par défaut)

J'écris une application dans Java qui se connecte à deux serveurs Web via HTTPS. L'un reçoit un certificat approuvé par la chaîne de confiance par défaut, l'autre utilise un certificat auto-signé. Bien sûr, la connexion sur le premier serveur fonctionnait immédiatement, alors que la connexion au serveur avec le certificat auto-signé ne fonctionnait pas avant la création d'un trustStore avec le certificat de ce serveur.Toutefois, la connexion au serveur de confiance par défaut ne fonctionne plus. , car apparemment, le trustStore par défaut est ignoré une fois que j’ai créé le mien.

Une solution que j'ai trouvée consistait à ajouter les certificats du trustStore par défaut à mon propre. Cependant, je n'aime pas cette solution, car elle nécessite que je continue à gérer ce trustStore. (Je ne peux pas supposer que ces certificats restent statiques dans un avenir prévisible, non?)

En dehors de cela, j'ai trouvé deux discussions de 5 ans avec un problème similaire:

Enregistrement de plusieurs magasins de clés dans la machine virtuelle Java

Comment puis-je avoir plusieurs certificats SSL pour un Java

Ils ont tous deux une connaissance approfondie de l'infrastructure SSL Java). J'espérais qu'il existe désormais une solution plus pratique que je peux facilement expliquer lors d'une révision de la sécurité de mon code.

53
user1785730

Vous pouvez utiliser un modèle similaire à ce que j'ai mentionné dans un réponse précédente (pour un problème différent).

Pour obtenir le gestionnaire de confiance par défaut, créez un deuxième gestionnaire de confiance qui utilise votre propre magasin de confiance. Enveloppez-les tous les deux dans une implémentation de gestionnaire de confiance personnalisée que les délégués appellent aux deux (retombez sur l'autre quand un échoue).

TrustManagerFactory tmf = TrustManagerFactory
    .getInstance(TrustManagerFactory.getDefaultAlgorithm());
// Using null here initialises the TMF with the default trust store.
tmf.init((KeyStore) null);

// Get hold of the default trust manager
X509TrustManager defaultTm = null;
for (TrustManager tm : tmf.getTrustManagers()) {
    if (tm instanceof X509TrustManager) {
        defaultTm = (X509TrustManager) tm;
        break;
    }
}

FileInputStream myKeys = new FileInputStream("truststore.jks");

// Do the same with your trust store this time
// Adapt how you load the keystore to your needs
KeyStore myTrustStore = KeyStore.getInstance(KeyStore.getDefaultType());
myTrustStore.load(myKeys, "password".toCharArray());

myKeys.close();

tmf = TrustManagerFactory
    .getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(myTrustStore);

// Get hold of the default trust manager
X509TrustManager myTm = null;
for (TrustManager tm : tmf.getTrustManagers()) {
    if (tm instanceof X509TrustManager) {
        myTm = (X509TrustManager) tm;
        break;
    }
}

// Wrap it in your own class.
final X509TrustManager finalDefaultTm = defaultTm;
final X509TrustManager finalMyTm = myTm;
X509TrustManager customTm = new X509TrustManager() {
    @Override
    public X509Certificate[] getAcceptedIssuers() {
        // If you're planning to use client-cert auth,
        // merge results from "defaultTm" and "myTm".
        return finalDefaultTm.getAcceptedIssuers();
    }

    @Override
    public void checkServerTrusted(X509Certificate[] chain,
            String authType) throws CertificateException {
        try {
            finalMyTm.checkServerTrusted(chain, authType);
        } catch (CertificateException e) {
            // This will throw another CertificateException if this fails too.
            finalDefaultTm.checkServerTrusted(chain, authType);
        }
    }

    @Override
    public void checkClientTrusted(X509Certificate[] chain,
            String authType) throws CertificateException {
        // If you're planning to use client-cert auth,
        // do the same as checking the server.
        finalDefaultTm.checkClientTrusted(chain, authType);
    }
};


SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[] { customTm }, null);

// You don't have to set this as the default context,
// it depends on the library you're using.
SSLContext.setDefault(sslContext);

Vous n'êtes pas obligé de définir ce contexte comme contexte par défaut. La façon dont vous l'utilisez dépend de la bibliothèque client que vous utilisez (et d'où provient son fabrique de sockets).


Ceci étant dit, en principe, vous devez toujours mettre à jour le fichier de clés certifiées de toute façon. Le Java 7 Guide de référence JSSE contenait une "note importante" à ce sujet, qui a été rétrogradée à juste un "note" dans la version 8 du même guide :

Le JDK est livré avec un nombre limité de certificats racine approuvés dans le fichier Java-home/lib/security/cacerts. Comme indiqué dans les pages de référence de Keytool, il est de votre responsabilité de gérer (c'est-à-dire d'ajouter et de supprimer) les certificats contenus dans ce fichier si vous utilisez ce fichier comme magasin de clés de confiance.

En fonction de la configuration du certificat des serveurs que vous contactez, vous devrez peut-être ajouter des certificats racine supplémentaires. Obtenez les certificats racine spécifiques nécessaires auprès du fournisseur approprié.

63
Bruno