From f2bf4773e5efb286552d3fa7288eebcdfb31e086 Mon Sep 17 00:00:00 2001 From: "Neil A. Wilson" Date: Tue, 12 Nov 2019 19:53:23 -0600 Subject: [PATCH] manage-certificates check usability improvement Updated the manage-certificates check-certificate-usability command to add an additional check to see whether the certificate at the root of the chain is found in the JVM's set of trusted issuer certificates. If the CA certificate is not found in the JVM's default trust store, a notice will be displayed, but the tool will still complete with a success result. --- docs/release-notes.html | 9 +++ messages/unboundid-ldapsdk-cert.properties | 10 +++ .../util/ssl/cert/ManageCertificates.java | 67 +++++++++++++++++++ 3 files changed, 86 insertions(+) diff --git a/docs/release-notes.html b/docs/release-notes.html index bc63543cd..60b6b4889 100644 --- a/docs/release-notes.html +++ b/docs/release-notes.html @@ -56,6 +56,15 @@

Version 4.0.13



+
  • + Updated the manage-certificates check-certificate-usability command to add an + additional check to see whether the certificate at the root of the chain is + found in the JVM's set of trusted issuer certificates. If the CA certificate + is not found in the JVM's default trust store, a notice will be displayed, but + the tool will still complete with a success result. +

    +
  • +
  • Made the ManageCertificates.readCertificatesFromFile method public so that it can be used outside of the LDAP SDK. This method can be used to read a diff --git a/messages/unboundid-ldapsdk-cert.properties b/messages/unboundid-ldapsdk-cert.properties index 8dc7f26bd..17d08da1e 100644 --- a/messages/unboundid-ldapsdk-cert.properties +++ b/messages/unboundid-ldapsdk-cert.properties @@ -2086,6 +2086,16 @@ ERR_MANAGE_CERTS_CHECK_USABILITY_CHAIN_ISSUER_MISMATCH=ERROR: The \ INFO_MANAGE_CERTS_CHECK_USABILITY_CHAIN_COMPLETE=OK: The certificate chain \ is complete. Each subsequent certificate is the issuer for the previous \ certificate in the chain, and the chain ends with a self-signed certificate. +INFO_MANAGE_CERTS_CHECK_USABILITY_CA_TRUSTED_OK=OK: CA certificate ''{0}'' \ + was found in the JVM's default set of trusted certificates. Most clients \ + will likely trust this issuer. +NOTE_MANAGE_CERTS_CHECK_USABILITY_CA_NOT_IN_JVM_DEFAULT_TS=NOTICE: CA \ + certificate ''{0}'' was not found in the JVM's default set of trusted \ + certificates. Clients will likely need special configuration to trust this \ + certificate chain. +WARN_MANAGE_CERTS_CHECK_USABILITY_CHECK_CA_IN_TS_ERROR=WARNING: An error \ + occurred while attempting to determine whether CA certificate ''{0}'' is \ + contained in the JVM-default trust store: {1} INFO_MANAGE_CERTS_CHECK_USABILITY_CERT_SIGNATURE_VALID=OK: Certificate \ ''{0}'' has a valid signature. ERR_MANAGE_CERTS_CHECK_USABILITY_END_CERT_NOT_YET_VALID=ERROR: Certificate \ diff --git a/src/com/unboundid/util/ssl/cert/ManageCertificates.java b/src/com/unboundid/util/ssl/cert/ManageCertificates.java index 9e841b09b..3968a387a 100644 --- a/src/com/unboundid/util/ssl/cert/ManageCertificates.java +++ b/src/com/unboundid/util/ssl/cert/ManageCertificates.java @@ -8294,6 +8294,73 @@ else if ((chain.length == 1) || (! chain[chain.length - 1].isSelfSigned())) } + // If there are multiple certificates in the chain, and if the last + // certificate in the chain is self-signed, then check to see if it is + // contained in the JVM-default trust manager. If it isn't, then we'll + // display a notice, but we won't consider it a warning in and of itself. + if ((chain.length > 1) && chain[chain.length-1].isSelfSigned()) + { + final X509Certificate caCert = chain[chain.length-1]; + + try + { + final String jvmDefaultTrustStoreType = + inferKeystoreType(JVM_DEFAULT_CACERTS_FILE); + final KeyStore jvmDefaultTrustStore = + KeyStore.getInstance(jvmDefaultTrustStoreType); + try (FileInputStream inputStream = + new FileInputStream(JVM_DEFAULT_CACERTS_FILE)) + { + jvmDefaultTrustStore.load(inputStream, null); + } + + boolean found = false; + final Enumeration aliases = jvmDefaultTrustStore.aliases(); + while (aliases.hasMoreElements()) + { + final String jvmDefaultCertAlias = aliases.nextElement(); + if (jvmDefaultTrustStore.isCertificateEntry(jvmDefaultCertAlias)) + { + final Certificate c = + jvmDefaultTrustStore.getCertificate(jvmDefaultCertAlias); + final X509Certificate xc = new X509Certificate(c.getEncoded()); + if ((caCert.getSubjectDN().equals(xc.getSubjectDN())) && + Arrays.equals(caCert.getSignatureValue().getBits(), + xc.getSignatureValue().getBits())) + { + found = true; + break; + } + } + } + + if (found) + { + out(); + wrapOut(0, WRAP_COLUMN, + INFO_MANAGE_CERTS_CHECK_USABILITY_CA_TRUSTED_OK.get( + caCert.getSubjectDN())); + } + else + { + out(); + wrapOut(0, WRAP_COLUMN, + NOTE_MANAGE_CERTS_CHECK_USABILITY_CA_NOT_IN_JVM_DEFAULT_TS.get( + caCert.getSubjectDN())); + } + } + catch (final Exception e) + { + Debug.debugException(e); + err(); + wrapErr(0, WRAP_COLUMN, + WARN_MANAGE_CERTS_CHECK_USABILITY_CHECK_CA_IN_TS_ERROR.get( + caCert.getSubjectDN(), StaticUtils.getExceptionMessage(e))); + numWarnings++; + } + } + + // Make sure that the signature is valid for each certificate in the // chain. If any certificate has an invalid signature, then that's an // error.