From df2ae75ee2dc98837c49c5f7bcdeacf8150d9f11 Mon Sep 17 00:00:00 2001 From: John Gasper Date: Fri, 11 May 2018 00:30:45 -0700 Subject: [PATCH] adding detailed output (#6) --- README.md | 30 +++++- pom.xml | 10 +- src/main/java/net/unicon/Test.java | 152 +++++++++++++++++++++++++++-- 3 files changed, 182 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index be918ee..3519753 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ mvn clean package - Run: ``` -java -jar [timeout] +java -jar [--detailed] [timeout] ``` ## Sample @@ -72,3 +72,31 @@ Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to ... 18 more ``` +### Detailed (--forced) Dump + +``` +INFO - Received host address https://expired.badssl.com/ +INFO - Setting connection timeout to 5 second(s). +INFO - HTTPS Connection: setting custom handler +INFO - Trying to connect to https://expired.badssl.com/ +INFO - Response status code received 200 +INFO - Detected Truststore: SunJSSE +INFO - Trusted issuers found: 104 +INFO - Server provided certs: +INFO - subject: CN=*.badssl.com, OU=PositiveSSL Wildcard, OU=Domain Control Validated +INFO - issuer: CN=COMODO RSA Domain Validation Secure Server CA, O=COMODO CA Limited, L=Salford, ST=Greater Manchester, C=GB +INFO - expiration: Wed Apr 08 17:00:00 PDT 2015 - Sun Apr 12 16:59:59 PDT 2015 (invalid) +INFO - trust anchor not matched in trust store (which is expected of the host certificate that is part of a chain) +INFO - --- +INFO - subject: CN=COMODO RSA Domain Validation Secure Server CA, O=COMODO CA Limited, L=Salford, ST=Greater Manchester, C=GB +INFO - issuer: CN=COMODO RSA Certification Authority, O=COMODO CA Limited, L=Salford, ST=Greater Manchester, C=GB +INFO - expiration: Tue Feb 11 16:00:00 PST 2014 - Sun Feb 11 15:59:59 PST 2029 (valid) +INFO - trust anchor matched found: CN=COMODO RSA Certification Authority, O=COMODO CA Limited, L=Salford, ST=Greater Manchester, C=GB +INFO - --- +INFO - subject: CN=COMODO RSA Certification Authority, O=COMODO CA Limited, L=Salford, ST=Greater Manchester, C=GB +INFO - issuer: CN=AddTrust External CA Root, OU=AddTrust External TTP Network, O=AddTrust AB, C=SE +INFO - expiration: Tue May 30 03:48:38 PDT 2000 - Sat May 30 03:48:38 PDT 2020 (valid) +INFO - trust anchor matched found: CN=AddTrust External CA Root, OU=AddTrust External TTP Network, O=AddTrust AB, C=SE +INFO - --- +INFO - Completed. See the info above for details. +``` \ No newline at end of file diff --git a/pom.xml b/pom.xml index c973da0..5ab098b 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 net.unicon java-keystore-test - 0.0.1 + 0.1.0 Java keystore test jar Java CLI utility to execute outbound HTTPS calls. @@ -28,6 +28,14 @@ + + org.apache.maven.plugins + maven-compiler-plugin + + 1.6 + 1.6 + + diff --git a/src/main/java/net/unicon/Test.java b/src/main/java/net/unicon/Test.java index 0f6ef55..b5179e1 100644 --- a/src/main/java/net/unicon/Test.java +++ b/src/main/java/net/unicon/Test.java @@ -6,6 +6,7 @@ import org.springframework.boot.ApplicationRunner; import org.springframework.boot.SpringApplication; +import javax.net.ssl.*; import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.HttpURLConnection; @@ -14,11 +15,15 @@ import java.net.Proxy; import java.net.Proxy.Type; import java.net.InetSocketAddress; - - +import java.security.*; +import java.security.cert.*; +import java.security.cert.Certificate; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; public class Test implements ApplicationRunner { - String USAGE = "Usage: java Test [--proxy=] [timeout]"; + String USAGE = "Usage: java Test [--proxy=] [--forced] [timeout]"; protected final Logger logger = LoggerFactory.getLogger(this.getClass()); public static void main(String[] args) throws Exception { @@ -27,7 +32,7 @@ public static void main(String[] args) throws Exception { public void run(ApplicationArguments args) throws Exception { System.setProperty("java.net.preferIPv4Stack", "true"); - + URLConnection conn = null; try { if (args.getNonOptionArgs().size() != 1 && args.getNonOptionArgs().size() != 2) { logger.warn(USAGE); @@ -36,7 +41,7 @@ public void run(ApplicationArguments args) throws Exception { logger.info("Received host address " + args.getNonOptionArgs().get(0)); URL constructedUrl = new URL(args.getNonOptionArgs().get(0)); - URLConnection conn; + if (args.containsOption("proxy")) { if (args.getOptionValues("proxy").size() != 1) { logger.warn(USAGE); @@ -54,8 +59,14 @@ public void run(ApplicationArguments args) throws Exception { } else { conn.setConnectTimeout(5000); } + logger.info("Setting connection timeout to " + conn.getConnectTimeout() / 1000 + " second(s)."); + if (conn instanceof HttpsURLConnection && args.containsOption("forced")) { + ((HttpsURLConnection) conn).setSSLSocketFactory(getSSLContext().getSocketFactory()); + logger.info("HTTPS Connection: setting custom handler"); + } + logger.info("Trying to connect to " + args.getNonOptionArgs().get(0)); InputStreamReader reader = new InputStreamReader(conn.getInputStream(), "UTF-8"); BufferedReader in = new BufferedReader(reader); @@ -67,18 +78,143 @@ public void run(ApplicationArguments args) throws Exception { logger.info("Response status code received " + code); } + + + if (args.containsOption("forced")) { + connectionReport(conn); + logger.info("Completed. See the info above for details."); + } else{ + logger.info("Great! It worked."); + } + in.close(); reader.close(); - logger.info("Great! It worked."); - } catch (Exception e) { logger.info("Could not connect to the host address " + args.getNonOptionArgs().get(0)); logger.info("The error is: " + e.getMessage()); - logger.info("Here are the details:"); + logger.info("Here are the exception details:"); logger.error(e.getMessage(), e); + logger.info("Run again with '--forced'."); throw new RuntimeException(e); } + } + + protected void connectionReport(URLConnection connection) { + if (!(connection instanceof HttpsURLConnection)) { + logger.info("This is not an HTTPS-based connection."); + return; + } + + X509TrustManager[] trustManagers = getSystemTrustManagers(); + HttpsURLConnection httpsConnection = (HttpsURLConnection)connection; + Certificate[] certificates; + + try { + certificates = httpsConnection.getServerCertificates(); + } catch (SSLPeerUnverifiedException e) { + logger.error(e.getMessage(), e); + throw new RuntimeException(e); + } + + X509Certificate[] serverCertificates = Arrays.copyOf(certificates, certificates.length, X509Certificate[].class); + + logger.info("Server provided certs: "); + for (X509Certificate certificate : serverCertificates) { + + String validity; + try { + certificate.checkValidity(); + validity = "valid"; + } catch (CertificateExpiredException e) { + validity = "invalid"; + } catch (CertificateNotYetValidException e) { + validity = "invalid"; + } + + logger.info(" subject: {}", certificate.getSubjectDN().getName()); + logger.info(" issuer: {}", certificate.getIssuerDN().getName()); + logger.info(" expiration: {} - {} ({})", certificate.getNotBefore(), certificate.getNotAfter(), validity); + logger.info(" trust anchor {}", checkTrustedCertStatus(certificate, trustManagers)); + logger.info("---"); + } + } + + protected X509TrustManager[] getSystemTrustManagers() { + TrustManagerFactory trustManagerFactory = null; + try { + trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + trustManagerFactory.init((KeyStore)null); + } catch (NoSuchAlgorithmException e) { + + } catch (KeyStoreException e) { + + } + + logger.info("Detected Truststore: {}", trustManagerFactory.getProvider().getName()); + List x509TrustManagers = new ArrayList(); + + for (TrustManager trustManager : trustManagerFactory.getTrustManagers()) { + if (trustManager instanceof X509TrustManager) { + X509TrustManager x509TrustManager = (X509TrustManager)trustManager; + logger.info(" Trusted issuers found: " + x509TrustManager.getAcceptedIssuers().length); + + x509TrustManagers.add(x509TrustManager); + } + } + + return x509TrustManagers.toArray(new X509TrustManager[]{}); + } + protected String checkTrustedCertStatus(X509Certificate certificate, X509TrustManager[] trustManagers) { + + for (X509TrustManager trustManager : trustManagers) { + for (X509Certificate trustedCert : trustManager.getAcceptedIssuers()) { + try { + certificate.verify(trustedCert.getPublicKey()); + return "matched found: " + trustedCert.getIssuerDN().getName(); + + } catch (CertificateException e) { + logger.trace("{}: {}", trustedCert.getIssuerDN().getName(), e.getMessage()); + } catch (NoSuchAlgorithmException e) { + logger.trace("{}: {}", trustedCert.getIssuerDN().getName(), e.getMessage()); + } catch (InvalidKeyException e) { + logger.trace("{}: {}", trustedCert.getIssuerDN().getName(), e.getMessage()); + } catch (NoSuchProviderException e) { + logger.trace("{}: {}", trustedCert.getIssuerDN().getName(), e.getMessage()); + } catch (SignatureException e) { + logger.trace("{}: {}", trustedCert.getIssuerDN().getName(), e.getMessage()); + } + } + } + return "not matched in trust store (which is expected of the host certificate that is part of a chain)"; + } + + protected SSLContext getSSLContext() { + try { + SSLContext sslCtx = SSLContext.getInstance("TLS"); + sslCtx.init(null, new TrustManager[]{ new X509TrustManager() { + + private X509Certificate[] accepted; + + @Override + public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException { + } + + @Override + public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException { + accepted = xcs; + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return accepted; + } + }}, null); + + return sslCtx; + } catch (Exception e) { + throw new RuntimeException(e); + } } }