Utiliser un fichier de clés de confiance personnalisé en Java ainsi que celui par défaut

J’écris une application en Java qui se connecte à deux serveurs Web via HTTPS. L’un d’eux a obtenu un certificate approuvé via la chaîne de confiance par défaut, l’autre utilise un certificate auto-signé. Bien entendu, la connexion au premier serveur a fonctionné à partir de la boîte, alors que la connexion au serveur avec le certificate auto-signé n’a pas fonctionné tant que j’ai créé un trustStore avec le certificate de ce serveur. Cependant, la connexion au serveur sécurisé par défaut ne fonctionne plus, car le fichier trustStore par défaut semble être ignoré une fois que j’ai créé le mien.

Une des solutions que j’ai trouvée consistait à append les certificates du fichier trustStore par défaut au mien. Cependant, je n’aime pas cette solution, car cela me demande de continuer à gérer ce trustStore. (Je ne peux pas supposer que ces certificates restnt statiques dans un avenir prévisible, n’est-ce pas?)

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

Enregistrement de plusieurs magasins de clés dans JVM

Comment puis-je avoir plusieurs certificates SSL pour un serveur Java?

Ils vont tous deux profondément dans l’infrastructure SSL Java. J’espérais que maintenant, il existe une solution plus pratique que je peux expliquer facilement dans une revue de sécurité de mon code.

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

Essentiellement, obtenez le gestionnaire de confiance par défaut, créez un second gestionnaire de confiance qui utilise votre propre magasin de clés de confiance. Encapsulez-les tous les deux dans une implémentation de gestionnaire de confiance personnalisée que les delegates appellent les deux (en retombant sur l’autre en cas d’échec).

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, Ssortingng 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, Ssortingng 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’avez pas besoin de définir ce contexte comme contexte par défaut. La manière dont vous l’utilisez dépend de la bibliothèque client que vous utilisez (et de ses sources de socket).


Cela étant dit, vous devez en principe toujours mettre à jour le fichier de clés de confiance, selon les besoins. Le Guide de référence de Java 7 JSSE contenait une “note importante” à ce sujet, désormais réduite à une simple “note” dans la version 8 du même guide :

Le JDK est livré avec un nombre limité de certificates racine approuvés dans le fichier java-home / lib / security / cacerts. Comme indiqué dans les pages de référence de Keytool, il vous incombe de gérer (c’est-à-dire d’append et de supprimer) les certificates contenus dans ce fichier si vous utilisez ce fichier comme fichier de clés certifiées.

Selon la configuration du certificate des serveurs que vous contactez, vous devrez peut-être append des certificates racine supplémentaires. Obtenez les certificates racine spécifiques nécessaires auprès du fournisseur approprié.