All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.unboundid.ldap.sdk.unboundidds.TopologyRegistryTrustManager Maven / Gradle / Ivy

/*
 * Copyright 2020 Ping Identity Corporation
 * All Rights Reserved.
 */
/*
 * Copyright 2020 Ping Identity Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 * Copyright (C) 2020 Ping Identity Corporation
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License (GPLv2 only)
 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see .
 */
package com.unboundid.ldap.sdk.unboundidds;



import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import javax.net.ssl.X509TrustManager;
import javax.security.auth.x500.X500Principal;

import com.unboundid.ldap.sdk.Attribute;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldif.LDIFException;
import com.unboundid.ldif.LDIFReader;
import com.unboundid.util.Base64;
import com.unboundid.util.Debug;
import com.unboundid.util.NotNull;
import com.unboundid.util.StaticUtils;
import com.unboundid.util.ThreadSafety;
import com.unboundid.util.ThreadSafetyLevel;

import static com.unboundid.ldap.sdk.unboundidds.UnboundIDDSMessages.*;



/**
 * This class provides an implementation of an X.509 trust manager that can be
 * used to trust certificates listed in the topology registry of a Ping Identity
 * Directory Server instance.  It will read the topology registry from the
 * server's configuration file rather than communicating with it over LDAP, so
 * it is only available for use when run from LDAP tools provided with the
 * Ping Identity Directory Server.
 * 
*
* NOTE: This class, and other classes within the * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only * supported for use against Ping Identity, UnboundID, and * Nokia/Alcatel-Lucent 8661 server products. These classes provide support * for proprietary functionality or for external specifications that are not * considered stable or mature enough to be guaranteed to work in an * interoperable way with other types of LDAP servers. *
*/ @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) public final class TopologyRegistryTrustManager implements X509TrustManager, Serializable { /** * The name of the object class that will be used in entries that may provide * information about inter-server certificates in the topology registry. */ @NotNull private static final String INTER_SERVER_CERT_OC = "ds-cfg-server-instance"; /** * The name of the attribute type for attributes that provide information * about inter-server certificates in the topology registry. */ @NotNull private static final String INTER_SERVER_CERT_AT = "ds-cfg-inter-server-certificate"; /** * The name of the object class that will be used in entries that may provide * information about listener certificates in the topology registry. */ @NotNull private static final String LISTENER_CERT_OC = "ds-cfg-server-instance-listener"; /** * The name of the attribute type for attributes that provide information * about listener certificates in the topology registry. */ @NotNull private static final String LISTENER_CERT_AT = "ds-cfg-listener-certificate"; /** * A pre-allocated empty certificate array. */ @NotNull static final X509Certificate[] NO_CERTIFICATES = new X509Certificate[0]; /** * The serial version UID for this serializable class. */ private static final long serialVersionUID = -1535917071172094611L; // The time that the cached certificates will expire. @NotNull private final AtomicLong cacheExpirationTime; // The certificates that have been cached. @NotNull private final AtomicReference> cachedCertificates; // The configuration file from which the certificate records will be read. @NotNull private final File configurationFile; // The maximum length of time in milliseconds that previously loaded // certificates may be cached. private final long cacheDurationMillis; /** * Creates a new instance of this trust manager with the provided settings. * * @param configurationFile The configuration file for the Ping Identity * Directory Server instance that holds the * topology registry data. * @param cacheDurationMillis The maximum length of time in milliseconds * that previously loaded certificates may be * cached. If this is less than or equal to * zero, then certificates will not be cached. */ public TopologyRegistryTrustManager(@NotNull final File configurationFile, final long cacheDurationMillis) { this.configurationFile = configurationFile; this.cacheDurationMillis = cacheDurationMillis; cacheExpirationTime = new AtomicLong(0L); cachedCertificates = new AtomicReference<>( Collections.emptySet()); } /** * Checks to determine whether the provided client certificate chain should be * trusted. * * @param chain The client certificate chain for which to make the * determination. * @param authType The authentication type based on the client certificate. * * @throws CertificateException If the provided client certificate chain * should not be trusted. */ @Override() public void checkClientTrusted(@NotNull final X509Certificate[] chain, @NotNull final String authType) throws CertificateException { checkTrusted(chain); } /** * Checks to determine whether the provided server certificate chain should be * trusted. * * @param chain The server certificate chain for which to make the * determination. * @param authType The key exchange algorithm used. * * @throws CertificateException If the provided server certificate chain * should not be trusted. */ @Override() public void checkServerTrusted(@NotNull final X509Certificate[] chain, @NotNull final String authType) throws CertificateException { checkTrusted(chain); } /** * Ensures that the provided certificate chain should be trusted. * * @param chain The certificate chain to validated. * * @throws CertificateException If the certificate chain should not be * trusted. */ private void checkTrusted(@NotNull final X509Certificate[] chain) throws CertificateException { // Make sure that the chain is not null or empty. if ((chain == null) || (chain.length == 0)) { throw new CertificateException(ERR_TR_TM_NO_CHAIN.get()); } // Validate that the peer certificate is currently within its validity // window. final long currentTime = System.currentTimeMillis(); final X509Certificate peerCert = chain[0]; if (currentTime < peerCert.getNotBefore().getTime()) { throw new CertificateException(ERR_TR_TM_PEER_NOT_YET_VALID.get( peerCert.getSubjectX500Principal().getName(X500Principal.RFC2253), String.valueOf(peerCert.getNotBefore()))); } if (currentTime > peerCert.getNotAfter().getTime()) { throw new CertificateException(ERR_TR_TM_PEER_EXPIRED.get( peerCert.getSubjectX500Principal().getName(X500Principal.RFC2253), String.valueOf(peerCert.getNotAfter()))); } // Validate that all of the issuer certificates are also valid. for (int i=1; i < chain.length; i++) { final X509Certificate issuerCert = chain[i]; if (currentTime < issuerCert.getNotBefore().getTime()) { throw new CertificateException(ERR_TR_TM_ISSUER_NOT_YET_VALID.get( peerCert.getSubjectX500Principal().getName(X500Principal.RFC2253), issuerCert.getSubjectX500Principal().getName( X500Principal.RFC2253), String.valueOf(peerCert.getNotBefore()))); } if (currentTime > issuerCert.getNotAfter().getTime()) { throw new CertificateException(ERR_TR_TM_ISSUER_EXPIRED.get( peerCert.getSubjectX500Principal().getName(X500Principal.RFC2253), issuerCert.getSubjectX500Principal().getName( X500Principal.RFC2253), String.valueOf(peerCert.getNotAfter()))); } } // If the cache is valid and it contains the certificate, then we'll trust // it. if ((cacheExpirationTime.get() >= currentTime) && cachedCertificates.get().contains(peerCert)) { // The certificate is trusted. Return without throwing an exception. return; } // Read the config file and get the certificates it contains. final Set topologyRegistryCertificates = readTopologyRegistryCertificates(peerCert); if (cacheDurationMillis > 0L) { cachedCertificates.set(topologyRegistryCertificates); cacheExpirationTime.set(currentTime + cacheDurationMillis); } if (topologyRegistryCertificates.contains(peerCert)) { // The certificate is trusted. Return without throwing an exception. return; } else { throw new CertificateException(ERR_TP_TM_PEER_NOT_FOUND.get( peerCert.getSubjectX500Principal().getName(X500Principal.RFC2253))); } } /** * Reads the certificates defined in the topology registry. * * @param peerCert The peer certificate presented for evaluation. * * @return A set containing the certificates defined in the topology * registry, or an empty set if no certificates are found. * * @throws CertificateException If a problem is encountered while reading * certificates from the topology registry. */ @NotNull() private Set readTopologyRegistryCertificates( @NotNull final X509Certificate peerCert) throws CertificateException { try (LDIFReader ldifReader = new LDIFReader(configurationFile)) { final Set certs = new HashSet<>(); while (true) { final Entry entry; try { entry = ldifReader.readEntry(); } catch (final LDIFException e) { Debug.debugException(e); if (e.mayContinueReading()) { continue; } else { throw new CertificateException( ERR_TP_TM_MALFORMED_CONFIG.get( peerCert.getSubjectX500Principal().getName( X500Principal.RFC2253), configurationFile.getAbsolutePath(), StaticUtils.getExceptionMessage(e)), e); } } if (entry == null) { return Collections.unmodifiableSet(certs); } if (entry.hasObjectClass(INTER_SERVER_CERT_OC) && entry.hasAttribute(INTER_SERVER_CERT_AT)) { parseCertificates(certs, entry.getAttribute(INTER_SERVER_CERT_AT)); } else if (entry.hasObjectClass(LISTENER_CERT_OC) && entry.hasAttribute(LISTENER_CERT_AT)) { parseCertificates(certs, entry.getAttribute(LISTENER_CERT_AT)); } } } catch (final IOException e) { Debug.debugException(e); throw new CertificateException( ERR_TP_TM_ERROR_READING_CONFIG_FILE.get( peerCert.getSubjectX500Principal().getName( X500Principal.RFC2253), configurationFile.getAbsolutePath(), StaticUtils.getExceptionMessage(e)), e); } } /** * Parses any values of the provided attribute as a set of X.509 certificates. * * @param certs The set that should be updated with the certificates that * are parsed. * @param attr The attribute whose values should be parsed. */ private void parseCertificates(@NotNull final Set certs, @NotNull final Attribute attr) { final StringBuilder certBase64 = new StringBuilder(); for (final String value : attr.getValues()) { try { for (final String line : StaticUtils.stringToLines(value)) { if (line.equalsIgnoreCase("-----BEGIN CERTIFICATE-----")) { continue; } else if (line.equalsIgnoreCase("-----END CERTIFICATE-----")) { final byte[] certBytes = Base64.decode(certBase64.toString()); certBase64.setLength(0); certs.add((X509Certificate) CertificateFactory.getInstance("X.509"). generateCertificate(new ByteArrayInputStream(certBytes))); } else { certBase64.append(line); } } } catch (final Exception e) { Debug.debugException(e); } } } /** * Retrieves the accepted issuer certificates for this trust manager. * * @return The accepted issuer certificates for this trust manager, or an * empty set of accepted issuers if a problem was encountered while * initializing this trust manager. */ @Override() @NotNull() public X509Certificate[] getAcceptedIssuers() { return NO_CERTIFICATES; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy