From 2266af7e2a58b7394901ae6bfe3f7055a987b4b8 Mon Sep 17 00:00:00 2001 From: Denys Digtiar Date: Tue, 20 Mar 2018 16:47:14 +1000 Subject: [PATCH] JENKINS-49668 - Root CAs component is fixed. Links below provide some explanation example as to how to get the actual trust store for inspection. They were used to replace the current broken code which simply tries to enumerate empty KeyStore. https://github.com/jenkinsci/jenkins-scripts/pull/82/files https://stackoverflow.com/questions/8884831/listing-certificates-in-jvm-trust-store --- .../jenkins/support/impl/RootCAs.java | 49 ++++++++++++------- .../jenkins/support/impl/RootCAsTest.java | 21 ++++++++ 2 files changed, 51 insertions(+), 19 deletions(-) create mode 100644 src/test/java/com/cloudbees/jenkins/support/impl/RootCAsTest.java diff --git a/src/main/java/com/cloudbees/jenkins/support/impl/RootCAs.java b/src/main/java/com/cloudbees/jenkins/support/impl/RootCAs.java index ba657f7ec..3be2ff2d2 100644 --- a/src/main/java/com/cloudbees/jenkins/support/impl/RootCAs.java +++ b/src/main/java/com/cloudbees/jenkins/support/impl/RootCAs.java @@ -35,7 +35,11 @@ import hudson.model.Node; import hudson.security.Permission; import jenkins.model.Jenkins; +import jenkins.security.MasterToSlaveCallable; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; import java.io.BufferedWriter; import java.io.IOException; import java.io.OutputStream; @@ -44,19 +48,16 @@ import java.io.StringWriter; import java.security.KeyStore; import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.X509Certificate; import java.util.Collections; -import java.util.Enumeration; import java.util.Set; import java.util.WeakHashMap; -import jenkins.security.MasterToSlaveCallable; -/** - * @author schristou88 - */ @Extension public class RootCAs extends Component { - private final WeakHashMap certCache = new WeakHashMap(); + private final WeakHashMap certCache = new WeakHashMap<>(); @Override public boolean isSelectedByDefault() { @@ -104,8 +105,6 @@ public void writeTo(OutputStream os) throws IOException { out.println(getRootCA(node)); } catch (IOException e) { SupportLogFormatter.printStackTrace(e, out); - } catch (InterruptedException e) { - SupportLogFormatter.printStackTrace(e, out); } finally { out.flush(); } @@ -114,7 +113,7 @@ public void writeTo(OutputStream os) throws IOException { ); } - public String getRootCA(Node node) throws IOException, InterruptedException { + public String getRootCA(Node node) throws IOException { return AsyncResultCache.get(node, certCache, new GetRootCA(), "Root CA info", "N/A: Either no connection to node, or no cached result"); } @@ -135,18 +134,30 @@ public String call() { } public static void getRootCAList(StringWriter writer) { - KeyStore instance = null; try { - instance = KeyStore.getInstance(KeyStore.getDefaultType()); - Enumeration aliases = instance.aliases(); - while (aliases.hasMoreElements()) { - String s = aliases.nextElement(); - writer.append("========"); - writer.append("Alias: " + s); - writer.append(instance.getCertificate(s).getPublicKey().toString()); - writer.append("Trusted certificate: " + instance.isCertificateEntry(s)); + // Inspired by: + // https://github.com/jenkinsci/jenkins-scripts/pull/82/files + // https://stackoverflow.com/questions/8884831/listing-certificates-in-jvm-trust-store + final TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + trustManagerFactory.init((KeyStore) null); + TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); + for (int i = 0; i < trustManagers.length; i++) { + writer.append("===== Trust Manager ").append(String.valueOf(i)).append(" =====\n"); + TrustManager trustManager = trustManagers[i]; + if (trustManager instanceof X509TrustManager) { + final X509Certificate[] acceptedIssuers = ((X509TrustManager) trustManager).getAcceptedIssuers(); + writer.append("It is an X.509 Trust Manager containing ") + .append(String.valueOf(acceptedIssuers.length)) + .append(" certificates:\n"); + for (X509Certificate x509Certificate : acceptedIssuers) { + writer.append(x509Certificate.getSubjectX500Principal().toString()).append('\n'); + } + } else { + writer.append("Skipping as it is not an X.509 Trust Manager.\n"); + writer.append("Class Name: ").append(trustManager.getClass().getName()).append('\n'); + } } - } catch (KeyStoreException e) { + } catch (KeyStoreException | NoSuchAlgorithmException e) { writer.write(Functions.printThrowable(e)); } } diff --git a/src/test/java/com/cloudbees/jenkins/support/impl/RootCAsTest.java b/src/test/java/com/cloudbees/jenkins/support/impl/RootCAsTest.java new file mode 100644 index 000000000..ad09ee57b --- /dev/null +++ b/src/test/java/com/cloudbees/jenkins/support/impl/RootCAsTest.java @@ -0,0 +1,21 @@ +package com.cloudbees.jenkins.support.impl; + +import org.junit.Test; + +import java.io.StringWriter; + +import static org.hamcrest.Matchers.startsWith; +import static org.junit.Assert.assertThat; + +public class RootCAsTest { + + @Test + public void getRootCAList() { + final StringWriter certsWriter = new StringWriter(); + RootCAs.getRootCAList(certsWriter); + final String rootCAs = certsWriter.toString(); + + assertThat("output doesn't start with the Exception", + rootCAs, startsWith("===== Trust Manager 0 =====\n")); + } +}