use of com.unboundid.util.ssl.cert.CertException in project ldapsdk by pingidentity.
the class PEMFileKeyManager method readCertificateChain.
/**
* Reads the certificate chain from the provided PEM files.
*
* @param certificateChainPEMFiles The files containing the PEM-formatted
* X.509 representations of the certificates
* in the certificate chain. This must not
* be {@code null} or empty. Each file must
* exist and must contain at least one
* certificate. The files will be processed
* in the order in which they are provided.
* The first certificate in the first file
* must be the end entity certificate, and
* each subsequent certificate must be the
* issuer for the previous certificate. The
* chain does not need to be complete as
* long as the peer may be expected to have
* prior knowledge of any missing issuer
* certificates.
*
* @return The certificate chain that was read.
*
* @throws KeyStoreException If a problem is encountered while reading the
* certificate chain.
*/
@NotNull()
private static X509Certificate[] readCertificateChain(@NotNull final List<File> certificateChainPEMFiles) throws KeyStoreException {
com.unboundid.util.ssl.cert.X509Certificate lastCert = null;
final List<X509Certificate> certList = new ArrayList<>();
for (final File f : certificateChainPEMFiles) {
if (!f.exists()) {
throw new KeyStoreException(ERR_PEM_FILE_KEY_MANAGER_NO_SUCH_CERT_FILE.get(f.getAbsolutePath()));
}
boolean readCert = false;
try (final X509PEMFileReader r = new X509PEMFileReader(f)) {
while (true) {
final com.unboundid.util.ssl.cert.X509Certificate c = r.readCertificate();
if (c == null) {
if (!readCert) {
throw new KeyStoreException(ERR_PEM_FILE_KEY_MANAGER_EMPTY_CERT_FILE.get(f.getAbsolutePath()));
}
break;
}
readCert = true;
if ((lastCert != null) && (!c.isIssuerFor(lastCert))) {
throw new KeyStoreException(ERR_PEM_FILE_KEY_MANAGER_SUBSEQUENT_CERT_NOT_ISSUER.get(c.getSubjectDN().toString(), f.getAbsolutePath(), lastCert.getSubjectDN().toString()));
}
try {
certList.add((X509Certificate) c.toCertificate());
} catch (final Exception e) {
Debug.debugException(e);
throw new KeyStoreException(ERR_PEM_FILE_KEY_MANAGER_CANNOT_DECODE_CERT.get(c.getSubjectDN().toString(), f.getAbsolutePath(), StaticUtils.getExceptionMessage(e)), e);
}
lastCert = c;
}
} catch (final KeyStoreException e) {
Debug.debugException(e);
throw e;
} catch (final IOException e) {
Debug.debugException(e);
throw new KeyStoreException(ERR_PEM_FILE_KEY_MANAGER_ERROR_READING_FROM_FILE.get(f.getAbsolutePath(), StaticUtils.getExceptionMessage(e)), e);
} catch (final CertException e) {
Debug.debugException(e);
throw new KeyStoreException(ERR_PEM_FILE_KEY_MANAGER_ERROR_READING_CERT.get(f.getAbsolutePath(), e.getMessage()), e);
}
}
final X509Certificate[] chain = new X509Certificate[certList.size()];
return certList.toArray(chain);
}
use of com.unboundid.util.ssl.cert.CertException in project ldapsdk by pingidentity.
the class PEMFileTrustManager method checkTrusted.
/**
* Determines whether the provided certificate chain should be considered
* trusted based on the trusted certificate information read from PEM files.
* Note that this method assumes that the trusted certificate information read
* from PEM files should be authoritative, and therefore doesn't perform some
* types of validation (like ensuring that all issuer certificates are trusted
* rather than validating that at least one is trusted, or checking extensions
* like basic constraints).
*
* @param chain The certificate chain for which to make the determination.
* It must not be {@code null} or empty.
*
* @throws CertificateException If the provided certificate chain should not
* be considered trusted.
*/
private void checkTrusted(@NotNull final X509Certificate[] chain) throws CertificateException {
// If the chain is null or empty, then it cannot be trusted.
if ((chain == null) || (chain.length == 0)) {
throw new CertificateException(ERR_PEM_FILE_TRUST_MANAGER_EMPTY_CHAIN.get());
}
// Iterate through all the certificates in the chain, parsing them using the
// LDAP SDK's X.509 certificate representation, and performing all of the
// following validation:
//
// - Make sure that the certificate is within the validity window.
//
// - Make sure that each subsequent certificate in the chain is the issuer
// for the previous certificate.
//
// - Check to see whether at least one of the certificates in the chain
// matches one read from the set of PEM files.
boolean foundCertificate = false;
com.unboundid.util.ssl.cert.X509Certificate firstCertificate = null;
com.unboundid.util.ssl.cert.X509Certificate previousCertificate = null;
for (final X509Certificate c : chain) {
final com.unboundid.util.ssl.cert.X509Certificate parsedCertificate;
try {
parsedCertificate = new com.unboundid.util.ssl.cert.X509Certificate(c.getEncoded());
} catch (final CertException e) {
Debug.debugException(e);
throw new CertificateException(ERR_PEM_FILE_TRUST_MANAGER_CANNOT_PARSE_CERT_FROM_CHAIN.get(c.getSubjectX500Principal().getName(X500Principal.RFC2253), StaticUtils.getExceptionMessage(e)), e);
}
if (firstCertificate == null) {
firstCertificate = parsedCertificate;
}
if (!parsedCertificate.isWithinValidityWindow()) {
throw new CertificateException(ERR_PEM_FILE_TRUST_MANAGER_CERT_NOT_VALID.get(String.valueOf(parsedCertificate.getSubjectDN()), StaticUtils.encodeRFC3339Time(parsedCertificate.getNotBeforeDate()), StaticUtils.encodeRFC3339Time(parsedCertificate.getNotAfterDate())));
}
if ((previousCertificate != null) && (!parsedCertificate.isIssuerFor(previousCertificate))) {
throw new CertificateException(ERR_PEM_FILE_TRUST_MANAGER_CERT_NOT_ISSUER.get(String.valueOf(parsedCertificate.getSubjectDN()), String.valueOf(previousCertificate.getSubjectDN())));
}
foundCertificate |= trustedCertificates.containsKey(parsedCertificate);
previousCertificate = parsedCertificate;
}
// the certificates in the trust store were an issuer for that certificate.
if ((!foundCertificate) && (!previousCertificate.isSelfSigned())) {
for (final com.unboundid.util.ssl.cert.X509Certificate c : trustedCertificates.keySet()) {
if (c.isIssuerFor(previousCertificate)) {
foundCertificate = true;
break;
}
}
}
if (!foundCertificate) {
throw new CertificateException(ERR_PEM_FILE_TRUST_MANAGER_NOT_TRUSTED.get(String.valueOf(firstCertificate.getSubjectDN())));
}
}
use of com.unboundid.util.ssl.cert.CertException in project ldapsdk by pingidentity.
the class CertificateDataReplaceCertificateKeyStoreContent method readPEMCertificates.
/**
* Reads one or more PEM-formatted X.509 certificates from the given input
* stream.
*
* @param file The file with which the provided input stream is
* associated. It must not be {@code null}.
* @param inputStream The input stream from which the certificates are to
* be read. It must not be {@code null}.
* @param encodedCerts A list that will be updated with the certificates
* that are read. This must not be {@code null} and
* must be updatable.
*
* @throws IOException If a problem occurs while trying to read from the
* file.
*
* @throws LDAPException If the contents of the file cannot be parsed as a
* valid set of PEM-formatted certificates.
*/
private static void readPEMCertificates(@NotNull final File file, @NotNull final InputStream inputStream, @NotNull final List<byte[]> encodedCerts) throws IOException, LDAPException {
try (X509PEMFileReader pemReader = new X509PEMFileReader(inputStream)) {
while (true) {
final X509Certificate cert = pemReader.readCertificate();
if (cert == null) {
return;
}
encodedCerts.add(cert.getX509CertificateBytes());
}
} catch (final CertException e) {
Debug.debugException(e);
throw new LDAPException(ResultCode.DECODING_ERROR, ERR_CD_KSC_DECODE_PEM_CERT_ERROR.get(file.getAbsolutePath(), e.getMessage()), e);
}
}
use of com.unboundid.util.ssl.cert.CertException in project ldapsdk by pingidentity.
the class PromptTrustManagerProcessor method shouldPrompt.
/**
* Indicates whether the trust manager should prompt about whether to trust
* the provided certificate chain.
*
* @param cacheKey The key that should be used to identify
* this certificate chain in the cache. It
* should be an all-lowercase hexadecimal
* representation of the end certificate's
* subject.
* @param chain The certificate chain to be examined. It
* must not be {@code null} or empty.
* @param isServerChain Indicates whether the provided certificate
* chain was provided by a server (if
* {@code true}) or a client (if
* {@code false}).
* @param examineValidityDates Indicates whether to examine the
* certificate's validity dates in the course
* of determining about whether to prompt
* about whether to trust the given
* certificate chain.
* @param acceptedCertificates A cache of the certificates that have
* already been accepted. The entries in the
* cache will be mapped from an all-lowercase
* hex representation of a previously-trusted
* certificate's signature to a Boolean value
* that indicates whether the certificate has
* been declared trusted even if the
* certificate is outside the validity
* window.
* @param expectedServerAddresses A list containing the addresses that the
* client is expected to use to connect to a
* target server. If this is {@code null} or
* empty, then no address validation will be
* performed. This will be ignored if
* {@code isServerChain} is {@code false}.
*
* @return An object pair in which the first element is a {@code Boolean}
* indicating whether the trust manager should prompt about whether
* to trust the certificate chain, and the second element is a
* (possibly empty) list of warning messages about the certificate
* chain that should be displayed to the user if they should be
* prompted.
*/
@NotNull()
static ObjectPair<Boolean, List<String>> shouldPrompt(@NotNull final String cacheKey, @NotNull final X509Certificate[] chain, final boolean isServerChain, final boolean examineValidityDates, @NotNull final Map<String, Boolean> acceptedCertificates, @Nullable final List<String> expectedServerAddresses) {
// See if any of the certificates is outside the validity window.
boolean outsideValidityWindow = false;
final List<String> warningMessages = new ArrayList<>(5);
final long currentTime = System.currentTimeMillis();
for (int i = 0; i < chain.length; i++) {
if (!chain[i].isWithinValidityWindow(currentTime)) {
outsideValidityWindow = true;
final String identifier;
if (i == 0) {
if (isServerChain) {
identifier = WARN_PROMPT_PROCESSOR_LABEL_SERVER.get();
} else {
identifier = WARN_PROMPT_PROCESSOR_LABEL_CLIENT.get();
}
} else {
identifier = WARN_PROMPT_PROCESSOR_LABEL_ISSUER.get();
}
if (currentTime > chain[i].getNotAfterTime()) {
final long expiredSecondsAgo = Math.round(((currentTime - chain[i].getNotAfterTime()) / 1000.0d));
warningMessages.add(WARN_PROMPT_PROCESSOR_CERT_EXPIRED.get(identifier, String.valueOf(chain[i].getSubjectDN()), formatDate(chain[i].getNotAfterDate()), StaticUtils.secondsToHumanReadableDuration(expiredSecondsAgo)));
} else {
final long secondsUntilValid = Math.round(((chain[i].getNotBeforeTime() - currentTime) / 1000.0d));
warningMessages.add(WARN_PROMPT_PROCESSOR_CERT_NOT_YET_VALID.get(identifier, String.valueOf(chain[i].getSubjectDN()), formatDate(chain[i].getNotBeforeDate()), StaticUtils.secondsToHumanReadableDuration(secondsUntilValid)));
}
}
}
// If the certificate at the head of the chain has an extended key usage
// extension, then make sure it includes either the serverAuth usage (for a
// server certificate) or the clientAuth usage (for a client certificate).
SubjectAlternativeNameExtension san = null;
for (final X509CertificateExtension extension : chain[0].getExtensions()) {
if (extension instanceof ExtendedKeyUsageExtension) {
final ExtendedKeyUsageExtension eku = (ExtendedKeyUsageExtension) extension;
if (isServerChain) {
if (!eku.getKeyPurposeIDs().contains(ExtendedKeyUsageID.TLS_SERVER_AUTHENTICATION.getOID())) {
warningMessages.add(WARN_PROMPT_PROCESSOR_EKU_MISSING_SERVER_AUTH.get(chain[0].getSubjectDN()));
}
} else {
if (!eku.getKeyPurposeIDs().contains(ExtendedKeyUsageID.TLS_CLIENT_AUTHENTICATION.getOID())) {
warningMessages.add(WARN_PROMPT_PROCESSOR_EKU_MISSING_CLIENT_AUTH.get(chain[0].getSubjectDN()));
}
}
} else if (extension instanceof SubjectAlternativeNameExtension) {
san = (SubjectAlternativeNameExtension) extension;
}
}
// extensions in the issuer certificates.
if (chain.length == 1) {
if (chain[0].isSelfSigned()) {
warningMessages.add(WARN_PROMPT_PROCESSOR_CERT_IS_SELF_SIGNED.get());
try {
chain[0].verifySignature(chain[0]);
} catch (final CertException ce) {
Debug.debugException(ce);
warningMessages.add(ce.getMessage());
}
} else {
warningMessages.add(WARN_PROMPT_PROCESSOR_CHAIN_NOT_COMPLETE.get(chain[0].getSubjectDN()));
}
} else {
for (int i = 1; i < chain.length; i++) {
if (chain[i].isIssuerFor(chain[i - 1])) {
try {
chain[i - 1].verifySignature(chain[i]);
} catch (final CertException ce) {
Debug.debugException(ce);
warningMessages.add(ce.getMessage());
}
} else {
warningMessages.add(WARN_PROMPT_PROCESSOR_CHAIN_ISSUER_MISMATCH.get(chain[i].getSubjectDN(), chain[i - 1].getSubjectDN()));
}
BasicConstraintsExtension bc = null;
KeyUsageExtension ku = null;
for (final X509CertificateExtension extension : chain[i].getExtensions()) {
if (extension instanceof BasicConstraintsExtension) {
bc = (BasicConstraintsExtension) extension;
} else if (extension instanceof KeyUsageExtension) {
ku = (KeyUsageExtension) extension;
}
}
if (bc == null) {
warningMessages.add(WARN_PROMPT_PROCESSOR_NO_BC_EXTENSION.get(chain[i].getSubjectDN()));
} else if (!bc.isCA()) {
warningMessages.add(WARN_PROMPT_PROCESSOR_BC_NOT_CA.get(chain[i].getSubjectDN()));
} else if ((bc.getPathLengthConstraint() != null) && ((i - 1) > bc.getPathLengthConstraint())) {
if (bc.getPathLengthConstraint() == 0) {
warningMessages.add(WARN_PROMPT_PROCESSOR_BC_DISALLOWED_INTERMEDIATE.get(chain[i].getSubjectDN()));
} else {
warningMessages.add(WARN_PROMPT_PROCESSOR_BC_TOO_MANY_INTERMEDIATES.get(chain[i].getSubjectDN(), bc.getPathLengthConstraint(), (i - 1)));
}
}
if ((ku != null) && (!ku.isKeyCertSignBitSet())) {
warningMessages.add(WARN_PROMPT_PROCESSOR_KU_NO_KEY_CERT_SIGN.get(chain[i].getSubjectDN()));
}
}
if (chain[chain.length - 1].isSelfSigned()) {
try {
chain[chain.length - 1].verifySignature(chain[chain.length - 1]);
} catch (final CertException ce) {
Debug.debugException(ce);
warningMessages.add(ce.getMessage());
}
} else {
warningMessages.add(WARN_PROMPT_PROCESSOR_CHAIN_NOT_COMPLETE.get(chain[chain.length - 1].getSubjectDN()));
}
}
// addresses.
if (isServerChain && (expectedServerAddresses != null) && (!expectedServerAddresses.isEmpty())) {
// Get the CN attribute from the certificate subject.
boolean hasAllowedAddress = false;
final StringBuilder addressBuffer = new StringBuilder();
for (final RDN rdn : chain[0].getSubjectDN().getRDNs()) {
final String[] names = rdn.getAttributeNames();
for (int i = 0; i < names.length; i++) {
if (names[i].equalsIgnoreCase("cn") || names[i].equalsIgnoreCase("commonName") || names[i].equalsIgnoreCase("2.5.4.3")) {
final String cnValue = rdn.getAttributeValues()[i];
final String lowerCNValue = StaticUtils.toLowerCase(cnValue);
if (isHostnameOrIPAddress(lowerCNValue)) {
commaAppend(addressBuffer, cnValue);
if (isAllowedHostnameOrIPAddress(lowerCNValue, expectedServerAddresses)) {
hasAllowedAddress = true;
break;
}
}
}
}
if (hasAllowedAddress) {
break;
}
}
// check its DNS names.
if ((!hasAllowedAddress) && (san != null)) {
for (final String dnsName : san.getDNSNames()) {
commaAppend(addressBuffer, dnsName);
if (isAllowedHostnameOrIPAddress(dnsName, expectedServerAddresses)) {
hasAllowedAddress = true;
break;
}
}
if (!hasAllowedAddress) {
for (final InetAddress ipAddress : san.getIPAddresses()) {
commaAppend(addressBuffer, ipAddress.getHostAddress());
if (isAllowedIPAddress(ipAddress, expectedServerAddresses)) {
hasAllowedAddress = true;
break;
}
}
}
}
if (!hasAllowedAddress) {
if (addressBuffer.length() == 0) {
// The certificate doesn't indicate the server with which it should be
// used. This isn't desirable, but we won't warn about it.
} else if (addressBuffer.indexOf(",") > 0) {
warningMessages.add(WARN_PROMPT_PROCESSOR_MULTIPLE_ADDRESSES_NOT_MATCHED.get(chain[0].getSubjectDN(), addressBuffer));
} else {
warningMessages.add(WARN_PROMPT_PROCESSOR_SINGLE_ADDRESS_NOT_MATCHED.get(chain[0].getSubjectDN(), addressBuffer));
}
}
}
// See if the provided certificate is in the cache. If not, then we will
// definitely prompt. If the cache indicates that the certificate has been
// accepted even outside the validity window, then we will not prompt.
// Otherwise, we'll prompt only if the certificate is outside the validity
// window.
final Boolean acceptedEvenWithBadValidity = acceptedCertificates.get(cacheKey);
if (acceptedEvenWithBadValidity == null) {
return new ObjectPair<>(Boolean.TRUE, warningMessages);
} else if (acceptedEvenWithBadValidity) {
return new ObjectPair<>(Boolean.FALSE, warningMessages);
} else {
return new ObjectPair<>(outsideValidityWindow, warningMessages);
}
}
use of com.unboundid.util.ssl.cert.CertException in project ldapsdk by pingidentity.
the class PEMFileTrustManager method readTrustedCertificates.
/**
* Reads trusted certificate information from the specified PEM file.
*
* @param f The PEM file to examine. It must not be {@code null}, and it
* must reference a file that exists. If it is a directory, then
* all files contained in it (including subdirectories) will be
* recursively processed.
* @param m The map to be updated wth the certificates read from the PEM
* files. It must not be {@code null} and must be updatable.
*
* @throws KeyStoreException If a problem is encountered while reading
* trusted certificate information from the
* specified file.
*/
private static void readTrustedCertificates(@NotNull final File f, @NotNull final Map<com.unboundid.util.ssl.cert.X509Certificate, X509Certificate> m) throws KeyStoreException {
if (!f.exists()) {
throw new KeyStoreException(ERR_PEM_FILE_TRUST_MANAGER_NO_SUCH_FILE.get(f.getAbsolutePath()));
}
try {
if (f.isDirectory()) {
for (final File fileInDir : f.listFiles()) {
readTrustedCertificates(fileInDir, m);
}
} else {
try (X509PEMFileReader r = new X509PEMFileReader(f)) {
boolean readCert = false;
while (true) {
final com.unboundid.util.ssl.cert.X509Certificate cert = r.readCertificate();
if (cert == null) {
if (!readCert) {
throw new KeyStoreException(ERR_PEM_FILE_TRUST_MANAGER_EMPTY_FILE.get(f.getAbsolutePath()));
}
break;
}
readCert = true;
final X509Certificate c = (X509Certificate) cert.toCertificate();
m.put(cert, c);
}
}
}
} catch (final KeyStoreException e) {
Debug.debugException(e);
throw e;
} catch (final IOException e) {
Debug.debugException(e);
throw new KeyStoreException(ERR_PEM_FILE_TRUST_MANAGER_ERROR_READING_FILE.get(f.getAbsolutePath(), StaticUtils.getExceptionMessage(e)), e);
} catch (final CertException e) {
Debug.debugException(e);
throw new KeyStoreException(ERR_PEM_FILE_TRUST_MANAGER_ERROR_PARSING_CERT.get(f.getAbsolutePath(), e.getMessage()), e);
} catch (final Exception e) {
Debug.debugException(e);
throw new KeyStoreException(ERR_PEM_FILE_TRUST_MANAGER_ERROR_PROCESSING_FILE.get(f.getAbsolutePath(), StaticUtils.getExceptionMessage(e)), e);
}
}
Aggregations