From 8a6e3b7ae9c25f31a5e48034171aff0e9a9ec2c2 Mon Sep 17 00:00:00 2001
From: Chris Ridd <chris.ridd@forgerock.com>
Date: Tue, 12 Jul 2016 15:19:29 +0000
Subject: [PATCH] OPENDJ-2855 Check Subject Alt Names in the CheckHostName TrustManager

---
 opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert7.pem  |   18 +
 opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert2.pem  |   19 +
 opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert11.pem |   19 +
 opendj-core/src/main/java/org/forgerock/opendj/ldap/InetAddressValidator.java     |  217 ++++++++++++
 opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert13.pem |   20 +
 opendj-core/src/main/resources/com/forgerock/opendj/ldap/core.properties          |    9 
 opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert10.pem |   18 +
 opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert6.pem  |   18 +
 opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert4.pem  |   18 +
 opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert1.pem  |   18 +
 opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert8.pem  |   18 +
 opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert12.pem |   18 +
 opendj-core/src/test/java/org/forgerock/opendj/ldap/TrustManagersTestCase.java    |  218 ++++++++++++
 opendj-core/src/main/java/org/forgerock/opendj/ldap/TrustManagers.java            |  282 ++++++++++++++--
 opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert9.pem  |   19 +
 opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert3.pem  |   18 +
 opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert5.pem  |   18 +
 17 files changed, 922 insertions(+), 43 deletions(-)

diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/InetAddressValidator.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/InetAddressValidator.java
new file mode 100644
index 0000000..ab2c845
--- /dev/null
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/InetAddressValidator.java
@@ -0,0 +1,217 @@
+/*
+ * The contents of this file are subject to the terms of the Common Development and
+ * Distribution License (the License). You may not use this file except in compliance with the
+ * License.
+ *
+ * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
+ * specific language governing permission and limitations under the License.
+ *
+ * When distributing Covered Software, include this CDDL Header Notice in each file and include
+ * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
+ * Header, with the fields enclosed by brackets [] replaced by your own identifying
+ * information: "Portions Copyright [year] [name of copyright owner]".
+ *
+ * Portions copyright 2016 ForgeRock AS.
+ */
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+package org.forgerock.opendj.ldap;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * <p><b>InetAddress</b> validation and conversion routines (<code>java.net.InetAddress</code>).</p>
+ *
+ * <p>This class provides methods to validate a candidate IP address.
+ *
+ * @since Validator 1.4
+ */
+final class InetAddressValidator {
+
+    private static final int IPV4_MAX_OCTET_VALUE = 255;
+
+    private static final int MAX_UNSIGNED_SHORT = 0xffff;
+
+    private static final int BASE_16 = 16;
+
+    private static final String IPV4_REGEX = "^(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$";
+
+    // Max number of hex groups (separated by :) in an IPV6 address
+    private static final int IPV6_MAX_HEX_GROUPS = 8;
+
+    // Max hex digits in each IPv6 group
+    private static final int IPV6_MAX_HEX_DIGITS_PER_GROUP = 4;
+
+    /** IPv4 regex pattern. */
+    private static final Pattern IPV4_PATTERN = Pattern.compile(IPV4_REGEX);
+
+    /** Private constructor. */
+    private InetAddressValidator() {
+    }
+
+    /**
+     * Checks if the specified string is a valid IP address.
+     * @param inetAddress the string to validate
+     * @return true if the string validates as an IP address
+     */
+    public static boolean isValid(String inetAddress) {
+        return isValidInet4Address(inetAddress) || isValidInet6Address(inetAddress);
+    }
+
+    /**
+     * Validates an IPv4 address. Returns true if valid.
+     * @param inet4Address the IPv4 address to validate
+     * @return true if the argument contains a valid IPv4 address
+     */
+    public static boolean isValidInet4Address(String inet4Address) {
+        // verify that address conforms to generic IPv4 format
+        String[] groups = match(inet4Address);
+
+        if (groups == null) {
+            return false;
+        }
+
+        // verify that address subgroups are legal
+        for (String ipSegment : groups) {
+            if (ipSegment == null || ipSegment.length() == 0) {
+                return false;
+            }
+
+            int iIpSegment;
+
+            try {
+                iIpSegment = Integer.parseInt(ipSegment);
+            } catch (NumberFormatException e) {
+                return false;
+            }
+
+            if (iIpSegment > IPV4_MAX_OCTET_VALUE) {
+                return false;
+            }
+
+            if (ipSegment.length() > 1 && ipSegment.startsWith("0")) {
+                return false;
+            }
+
+        }
+
+        return true;
+    }
+
+    /**
+     * Validate a value against the set of regular expressions returning the array of matched groups. From the Apache
+     * RegexValidator class.
+     *
+     * @param value  The value to validate
+     * @return  String array of the matched groups, of {@code null} if invalid.
+     */
+    private static String[] match(String value) {
+        if (value == null) {
+            return null;
+        }
+        Matcher matcher = IPV4_PATTERN.matcher(value);
+        if (matcher.matches()) {
+            int count = matcher.groupCount();
+            String[] groups = new String[count];
+            for (int j = 0; j < count; j++) {
+                groups[j] = matcher.group(j + 1);
+            }
+            return groups;
+        }
+        return null;
+    }
+
+    /**
+     * Validates an IPv6 address. Returns true if valid.
+     * @param inet6Address the IPv6 address to validate
+     * @return true if the argument contains a valid IPv6 address
+     *
+     * @since 1.4.1
+     */
+    public static boolean isValidInet6Address(String inet6Address) {
+        boolean containsCompressedZeroes = inet6Address.contains("::");
+        if (containsCompressedZeroes && (inet6Address.indexOf("::") != inet6Address.lastIndexOf("::"))) {
+            return false;
+        }
+        if ((inet6Address.startsWith(":") && !inet6Address.startsWith("::"))
+                || (inet6Address.endsWith(":") && !inet6Address.endsWith("::"))) {
+            return false;
+        }
+        String[] octets = inet6Address.split(":");
+        if (containsCompressedZeroes) {
+            List<String> octetList = new ArrayList<>(Arrays.asList(octets));
+            if (inet6Address.endsWith("::")) {
+                // String.split() drops ending empty segments
+                octetList.add("");
+            } else if (inet6Address.startsWith("::") && !octetList.isEmpty()) {
+                octetList.remove(0);
+            }
+            octets = octetList.toArray(new String[octetList.size()]);
+        }
+        if (octets.length > IPV6_MAX_HEX_GROUPS) {
+            return false;
+        }
+        int validOctets = 0;
+        int emptyOctets = 0;
+        for (int index = 0; index < octets.length; index++) {
+            String octet = octets[index];
+            if (octet.length() == 0) {
+                emptyOctets++;
+                if (emptyOctets > 1) {
+                    return false;
+                }
+            } else {
+                emptyOctets = 0;
+                if (octet.contains(".")) { // contains is Java 1.5+
+                    if (!inet6Address.endsWith(octet)) {
+                        return false;
+                    }
+                    if (index > octets.length - 1 || index > 6) {  // CHECKSTYLE IGNORE MagicNumber
+                        // IPV4 occupies last two octets
+                        return false;
+                    }
+                    if (!isValidInet4Address(octet)) {
+                        return false;
+                    }
+                    validOctets += 2;
+                    continue;
+                }
+                if (octet.length() > IPV6_MAX_HEX_DIGITS_PER_GROUP) {
+                    return false;
+                }
+                int octetInt;
+                try {
+                    octetInt = Integer.valueOf(octet, BASE_16);
+                } catch (NumberFormatException e) {
+                    return false;
+                }
+                if (octetInt < 0 || octetInt > MAX_UNSIGNED_SHORT) {
+                    return false;
+                }
+            }
+            validOctets++;
+        }
+        if (validOctets < IPV6_MAX_HEX_GROUPS && !containsCompressedZeroes) {
+            return false;
+        }
+        return true;
+    }
+}
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/TrustManagers.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/TrustManagers.java
index 4770a4c..aa375a1 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/TrustManagers.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/TrustManagers.java
@@ -19,30 +19,48 @@
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
 import java.security.GeneralSecurityException;
 import java.security.KeyStore;
 import java.security.NoSuchAlgorithmException;
 import java.security.cert.CertificateException;
 import java.security.cert.CertificateExpiredException;
 import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.CertificateParsingException;
 import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Date;
-import java.util.logging.Level;
-import java.util.logging.Logger;
+import java.util.List;
+import java.util.Set;
 
 import javax.net.ssl.TrustManager;
 import javax.net.ssl.TrustManagerFactory;
 import javax.net.ssl.X509TrustManager;
+import javax.security.auth.x500.X500Principal;
 
+import org.forgerock.i18n.LocalizableMessage;
+import org.forgerock.i18n.slf4j.LocalizedLogger;
+import org.forgerock.opendj.ldap.schema.AttributeType;
 import org.forgerock.opendj.ldap.schema.Schema;
 import org.forgerock.util.Reject;
 
+import static com.forgerock.opendj.ldap.CoreMessages.ERR_CERT_NO_MATCH_IP;
+import static com.forgerock.opendj.ldap.CoreMessages.ERR_CERT_NO_MATCH_DNS;
+import static com.forgerock.opendj.ldap.CoreMessages.ERR_CERT_NO_MATCH_ALLOTHERS;
+import static com.forgerock.opendj.ldap.CoreMessages.ERR_CERT_NO_MATCH_SUBJECT;
+
+
 /** This class contains methods for creating common types of trust manager. */
 public final class TrustManagers {
 
+    private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
+
     /**
-     * An X509TrustManager which rejects certificate chains whose subject DN
-     * does not match a specified host name.
+     * An X509TrustManager which rejects certificate chains whose subject alternative names do not match the specified
+     * host name or IP address. The check may fall back to checking a hostname in the left-most CN of the certificate
+     * subject for backwards compatibility.
      */
     private static final class CheckHostName implements X509TrustManager {
 
@@ -75,15 +93,191 @@
         }
 
         /**
-         * Checks whether a host name matches the provided pattern. It accepts
-         * the use of wildcards in the pattern, e.g. {@code *.example.com}.
+         * Look in the SubjectAlternativeName for DNS names (wildcards are allowed) and IP addresses, and potentially
+         * fall back to checking CN in the subjectDN.
+         * <p>
+         * If DNS names and IP addresses do not match, and other SubjectAlternativeNames are present and critical, do
+         * not fall back checking CN.
+         * </p>
+         * <p>
+         * If DNS names and IP addresses do not match and the SubjectAlternativeNames are non-critical, fall back to
+         * checking CN.
+         * </p>
+         * @param chain X.509 certificate chain from the server
+         */
+        private void verifyHostName(final X509Certificate[] chain) throws CertificateException {
+            final X500Principal principal = chain[0].getSubjectX500Principal();
+            try {
+                final List<String> dnsNamePatterns = new ArrayList<>(0);
+                final List<String> ipAddresses = new ArrayList<>(0);
+                final List<Object> allOthers = new ArrayList<>(0);
+                getSanGeneralNames(chain[0], dnsNamePatterns, ipAddresses, allOthers);
+                final boolean sanIsCritical = getSanCriticality(chain[0]);
+
+                final InetAddress hostAddress = toIpAddress(hostName);
+                if (hostAddress != null) {
+                    if (verifyIpAddresses(hostAddress, ipAddresses, principal, sanIsCritical)) {
+                        return;
+                    }
+                } else {
+                    if (verifyDnsNamePatterns(hostName, dnsNamePatterns, principal, sanIsCritical)) {
+                        return;
+                    }
+                }
+                if (!allOthers.isEmpty() && sanIsCritical) {
+                    throw new CertificateException(ERR_CERT_NO_MATCH_ALLOTHERS.get(principal, hostName).toString());
+                }
+
+                final DN dn = DN.valueOf(principal.getName(), Schema.getCoreSchema());
+                final String certSubjectHostName = getLowestCommonName(dn);
+                /* Backwards compatibility: check wildcards in cn */
+                if (hostNameMatchesPattern(hostName, certSubjectHostName)) {
+                    return;
+                }
+                throw new CertificateException(ERR_CERT_NO_MATCH_SUBJECT.get(principal, hostName).toString());
+            } catch (final CertificateException e) {
+                logger.warn(LocalizableMessage.raw("Certificate verification problem for: " + principal), e);
+                throw e;
+            }
+        }
+
+        /**
+         * Collect the general names from a certificate's SubjectAlternativeName extension.
+         *
+         * General Names can contain: dnsNames, ipAddresses, rfc822Names, x400Addresses, directoryNames, ediPartyNames,
+         * uniformResourceIdentifiers, registeredIDs (OID), or otherNames (anything). See
+         * {@link X509Certificate#getSubjectAlternativeNames()} for details on how these values are encoded. We separate
+         * the dnsNames and ipAddresses (which we can try to match) from everything else (which we do not try to match.)
+         *
+         * @param subject  certificate
+         * @param dnsNames  list where the dnsNames will be added (may be empty)
+         * @param ipAddresses  list where the ipAddresses will be added (may be empty)
+         * @param allOthers  list where all other general names will be added (may be empty)
+         */
+        private void getSanGeneralNames(X509Certificate subject,
+                                        List<String> dnsNames, List<String> ipAddresses,
+                                        List<Object> allOthers) {
+            try {
+                Collection<List<?>> sans = subject.getSubjectAlternativeNames();
+                if (sans == null) {
+                    return;
+                }
+                for (List<?> san : sans) {
+                    switch ((Integer) san.get(0)) {
+                    case 2:
+                        dnsNames.add((String) san.get(1));
+                        break;
+                    case 7:
+                        ipAddresses.add((String) san.get(1));
+                        break;
+                    default:
+                        allOthers.add(san.get(1));
+                        break;
+                    }
+                }
+            } catch (CertificateParsingException e) {
+                /* do nothing */
+            }
+        }
+
+        /**
+         * Get the ASN.1 criticality of the SubjectAlternativeName extension.
+         *
+         * @param subject X509Certificate to check
+         * @return {@code true} if a subject alt name was found and was marked critical, {@code false} otherwise.
+         */
+        private boolean getSanCriticality(X509Certificate subject) {
+            Set<String> critSet = subject.getCriticalExtensionOIDs();
+            return critSet != null && critSet.contains("2.5.29.17");
+        }
+
+        /**
+         * Convert to an IP address without performing a DNS lookup.
+         *
+         * @param hostName  either an IP address string, or a host name
+         * @return {@code InetAddress} if hostName was an IPv4 or IPv6 address, or {@code null}
+         */
+        private static InetAddress toIpAddress(String hostName) {
+            try {
+                if (InetAddressValidator.isValid(hostName)) {
+                    return InetAddress.getByName(hostName);
+                }
+            } catch (UnknownHostException e) {
+                /* do nothing */
+            }
+            return null;
+        }
+
+        /**
+         * Verify an IP address in the list of IP addresses.
+         *
+         * @param hostAddress  IP address from the user
+         * @param ipAddresses  List of IP addresses from the certificate (may be empty)
+         * @param principal  Subject name from the certificate
+         * @param failureIsCritical  Should a verification failure throw a {@link CertificateException}
+         * @return {@code true} if the address is verified, {@code false} if the address was not verified.
+         * @throws CertificateException  if verification fails and {@code failureIsCritical} is {@code true}.
+         */
+        private boolean verifyIpAddresses(InetAddress hostAddress, List<String> ipAddresses, X500Principal principal,
+                                          boolean failureIsCritical) throws CertificateException {
+            if (!ipAddresses.isEmpty()) {
+                for (String address : ipAddresses) {
+                    try {
+                        if (InetAddress.getByName(address).equals(hostAddress)) {
+                            return true;
+                        }
+                    } catch (UnknownHostException e) {
+                        // do nothing
+                    }
+                }
+                if (failureIsCritical) {
+                    // RFC 5280 mentions:
+                            /* If the subject field
+                             * contains an empty sequence, then the issuing CA MUST include a
+                             * subjectAltName extension that is marked as critical.  When including
+                             * the subjectAltName extension in a certificate that has a non-empty
+                             * subject distinguished name, conforming CAs SHOULD mark the
+                             * subjectAltName extension as non-critical.
+                             */
+                    // Since SAN is critical, the subject is empty, so we cannot perform the next check anyway
+                    throw new CertificateException(ERR_CERT_NO_MATCH_IP.get(principal, hostName).toString());
+                }
+            }
+            return false;
+        }
+
+        /**
+         * Verify a hostname in the list of DNS name patterns.
+         *
+         * @param hostName  Host name from the user
+         * @param dnsNamePatterns  List of DNS name patterns from the certificate (may be empty)
+         * @param principal  Subject name from the certificate
+         * @param failureIsCritical  Should a verification failure throw a {@link CertificateException}
+         * @return {@code true} if the address is verified, {@code false} if the address was not verified.
+         * @throws CertificateException  If verification fails and {@code failureIsCritical} is {@code true}
+         */
+        private boolean verifyDnsNamePatterns(String hostName, List<String> dnsNamePatterns, X500Principal principal,
+                              boolean failureIsCritical) throws CertificateException {
+            for (String namePattern : dnsNamePatterns) {
+                if (hostNameMatchesPattern(hostName, namePattern)) {
+                    return true;
+                }
+            }
+            if (failureIsCritical) {
+                throw new CertificateException(ERR_CERT_NO_MATCH_DNS.get(principal, hostName).toString());
+            }
+            return false;
+        }
+
+        /**
+         * Checks whether a host name matches the provided pattern. It accepts the use of wildcards in the pattern,
+         * e.g. {@code *.example.com}.
          *
          * @param hostName
          *            The host name.
          * @param pattern
-         *            The host name pattern, which may contain wild cards.
-         * @return {@code true} if the host name matched the pattern, otherwise
-         *         {@code false}.
+         *            The host name pattern, which may contain wildcards.
+         * @return {@code true} if the host name matched the pattern, otherwise {@code false}.
          */
         private boolean hostNameMatchesPattern(final String hostName, final String pattern) {
             final String[] nameElements = hostName.split("\\.");
@@ -100,24 +294,22 @@
             return hostMatch;
         }
 
-        private void verifyHostName(final X509Certificate[] chain) {
-            try {
-                // TODO: NPE if root DN.
-                final DN dn =
-                        DN.valueOf(chain[0].getSubjectX500Principal().getName(), Schema
-                                .getCoreSchema());
-                final String certSubjectHostName =
-                        dn.iterator().next().iterator().next().getAttributeValue().toString();
-                if (!hostNameMatchesPattern(hostName, certSubjectHostName)) {
-                    throw new CertificateException(
-                            "The host name contained in the certificate chain subject DN \'"
-                                    + chain[0].getSubjectX500Principal()
-                                    + "' does not match the host name \'" + hostName + "'");
+        /**
+         * Find the lowest (left-most) cn in the DN, and return its value.
+         *
+         * @param subject the DN being searched
+         * @return the cn value, or {@code null} if no cn was found
+         */
+        private String getLowestCommonName(DN subject) {
+            AttributeType cn = Schema.getDefaultSchema().getAttributeType("cn");
+            for (RDN rdn : subject) {
+                for (AVA ava : rdn) {
+                    if (ava.getAttributeType().equals(cn)) {
+                        return ava.getAttributeValue().toString();
+                    }
                 }
-            } catch (final Throwable t) {
-                LOG.log(Level.WARNING, "Error parsing subject dn: "
-                        + chain[0].getSubjectX500Principal(), t);
             }
+            return null;
         }
     }
 
@@ -155,16 +347,14 @@
                 try {
                     c.checkValidity(currentDate);
                 } catch (final CertificateExpiredException e) {
-                    LOG.log(Level.WARNING, "Refusing to trust security" + " certificate \""
-                            + c.getSubjectDN().getName() + "\" because it" + " expired on "
-                            + String.valueOf(c.getNotAfter()));
-
+                    logger.warn(LocalizableMessage.raw(
+                            "Refusing to trust security certificate \'%s\' because it expired on %s",
+                            c.getSubjectDN().getName(), String.valueOf(c.getNotAfter())));
                     throw e;
                 } catch (final CertificateNotYetValidException e) {
-                    LOG.log(Level.WARNING, "Refusing to trust security" + " certificate \""
-                            + c.getSubjectDN().getName() + "\" because it" + " is not valid until "
-                            + String.valueOf(c.getNotBefore()));
-
+                    logger.warn(LocalizableMessage.raw(
+                            "Refusing to trust security  certificate \'%s\' because it is not valid until %s",
+                            c.getSubjectDN().getName(), String.valueOf(c.getNotBefore())));
                     throw e;
                 }
             }
@@ -226,23 +416,29 @@
         }
     }
 
-    private static final Logger LOG = Logger.getLogger(TrustManagers.class.getName());
-
     /**
-     * Wraps the provided {@code X509TrustManager} by adding additional
-     * validation which rejects certificate chains whose subject DN does not
-     * match the specified host name pattern. The pattern may contain
-     * wild-cards, for example {@code *.example.com}.
+     * Wraps the provided {@code X509TrustManager} by adding additional validation which rejects certificate chains
+     * whose subject alternative names do not match the specified host name or IP address. The check may fall back to
+     * checking a hostname in the left-most CN of the subjectDN for backwards compatibility.
+     *
+     * If the {@code hostName} is an IP address, only the {@code ipAddresses} field of the subject alternative name
+     * will be checked. Similarly if {@code hostName} is not an IP address, only the {@code dnsNames} of the subject
+     * alternative name will be checked.
+     *
+     * Host names can be matched using wild cards, for example {@code *.example.com}.
+     *
+     * If a critical subject alternative name doesn't match, verification will not fall back to checking the subjectDN
+     * and will <b>fail</b>. If a critical subject alternative name doesn't match and it contains other kinds of general
+     * names that cannot be checked verification will also <b>fail</b>.
      *
      * @param hostName
-     *            A host name which the RDN value contained in
-     *            certificate subject DNs must match.
+     *            The IP address or hostname used to connect to the LDAP server which will be matched against the
+     *            subject alternative name and possibly the subjectDN as described above.
      * @param trustManager
      *            The trust manager to be wrapped.
      * @return The wrapped trust manager.
      * @throws NullPointerException
-     *             If {@code trustManager} or {@code hostNamePattern} was
-     *             {@code null}.
+     *             If {@code trustManager} or {@code hostName} was {@code null}.
      */
     public static X509TrustManager checkHostName(final String hostName,
             final X509TrustManager trustManager) {
diff --git a/opendj-core/src/main/resources/com/forgerock/opendj/ldap/core.properties b/opendj-core/src/main/resources/com/forgerock/opendj/ldap/core.properties
index dfb8df2..8c52e87 100644
--- a/opendj-core/src/main/resources/com/forgerock/opendj/ldap/core.properties
+++ b/opendj-core/src/main/resources/com/forgerock/opendj/ldap/core.properties
@@ -1681,3 +1681,12 @@
  because the connection timeout period of %d ms was exceeded
 LOAD_BALANCER_EVENT_LISTENER_LOG_ONLINE=Connection factory '%s' is now operational
 LOAD_BALANCER_EVENT_LISTENER_LOG_OFFLINE=Connection factory '%s' is no longer operational: %s
+
+ERR_CERT_NO_MATCH_IP=The IP addresses in the critical subject alt name extension for '%s' \
+  do not match the address '%s'
+ERR_CERT_NO_MATCH_DNS=The DNS names in the critical subject alt name extension for '%s' \
+  do not match the host name '%s'
+ERR_CERT_NO_MATCH_ALLOTHERS=None of the remaining names in the critical subject alt name extension for '%s' \
+  can be matched against '%s'
+ERR_CERT_NO_MATCH_SUBJECT=The host name contained in the subject DN '%s' \
+  does not match the host name '%s'
diff --git a/opendj-core/src/test/java/org/forgerock/opendj/ldap/TrustManagersTestCase.java b/opendj-core/src/test/java/org/forgerock/opendj/ldap/TrustManagersTestCase.java
new file mode 100644
index 0000000..17b5256
--- /dev/null
+++ b/opendj-core/src/test/java/org/forgerock/opendj/ldap/TrustManagersTestCase.java
@@ -0,0 +1,218 @@
+/*
+ * The contents of this file are subject to the terms of the Common Development and
+ * Distribution License (the License). You may not use this file except in compliance with the
+ * License.
+ *
+ * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
+ * specific language governing permission and limitations under the License.
+ *
+ * When distributing Covered Software, include this CDDL Header Notice in each file and include
+ * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
+ * Header, with the fields enclosed by brackets [] replaced by your own identifying
+ * information: "Portions Copyright [year] [name of copyright owner]".
+ *
+ * Copyright 2016 ForgeRock AS.
+ */
+package org.forgerock.opendj.ldap;
+
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+import org.testng.Assert;
+
+import javax.net.ssl.X509TrustManager;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.logging.Level;
+
+/**
+ * This class defines a set of tests for the
+ * {@link org.forgerock.opendj.ldap.TrustManagers} class.
+ */
+@SuppressWarnings("javadoc")
+public class TrustManagersTestCase extends SdkTestCase  {
+    /**
+     * X509TrustManager test data provider with a variety of hostnames in subjects and subject alt names.
+     *
+     * @return The array of test data.
+     */
+    @DataProvider(name = "hostnameTestData")
+    public Object[][] createHostnameTestData() throws Exception {
+        // see comments in test cases for descriptions
+        X509Certificate cert1 = getTestCertificate("cert1.pem");
+        X509Certificate cert2 = getTestCertificate("cert2.pem");
+        X509Certificate cert3 = getTestCertificate("cert3.pem");
+        X509Certificate cert4 = getTestCertificate("cert4.pem");
+        X509Certificate cert5 = getTestCertificate("cert5.pem");
+        X509Certificate cert6 = getTestCertificate("cert6.pem");
+        X509Certificate cert7 = getTestCertificate("cert7.pem");
+        X509Certificate cert8 = getTestCertificate("cert8.pem");
+        X509Certificate cert9 = getTestCertificate("cert9.pem");
+        X509Certificate cert10 = getTestCertificate("cert10.pem");
+        X509Certificate cert11 = getTestCertificate("cert11.pem");
+        X509Certificate cert12 = getTestCertificate("cert12.pem");
+        X509Certificate cert13 = getTestCertificate("cert13.pem");
+        // @formatter:off
+        return new Object[][] {
+             /*
+              * cert1:
+              *   subject "cn=ldap.example.com,o=OpenDJ"
+              *   san (none)
+              */
+            { cert1, "ldap.example.com", true },
+            { cert1, "ldap2.example.com", false },
+            { cert1, "192.168.0.1", false },
+            { cert1, "2001:db8::1:0:0:1", false },
+            { cert1, "*.example.com", false },
+             /*
+              * cert2:
+              *   subject "email=info,cn=ldap.example.com,o=OpenDJ"
+              *   san (none)
+              */
+            { cert2, "ldap.example.com", true },
+            { cert2, "ldap2.example.com", false },
+            { cert2, "192.168.0.1", false },
+            { cert2, "2001:db8::1:0:0:1", false },
+            { cert2, "info", false },
+             /*
+              * cert3:
+              *   subject "cn=ldap.example.com,o=OpenDJ"
+              *   san [ dnsName "ldap.example.org"] critical
+              */
+            { cert3, "ldap.example.org", true },
+            { cert3, "ldap.example.com", false }, // critical so no fall back to testing subject DN
+            { cert3, "ldap2.example.org", false },
+            { cert3, "192.168.0.1", false },
+            { cert3, "2001:db8::1:0:0:1", false },
+             /*
+              * cert4:
+              *   subject "cn=ldap.example.com,o=OpenDJ"
+              *   san [ dnsName "ldap.example.org"] non-critical
+              */
+            { cert4, "ldap.example.org", true },
+            { cert4, "ldap.example.com", true }, // falls back to testing subject DN
+            { cert4, "ldap2.example.org", false },
+            { cert4, "192.168.0.1", false },
+            { cert4, "2001:db8::1:0:0:1", false },
+             /*
+              * cert5:
+              *   subject "cn=server,o=OpenDJ"
+              *   san [ dnsName "ldap1.example.com", "ldap2.example.com" ] critical
+              */
+            { cert5, "ldap.example.com", false },
+            { cert5, "server", false },
+            { cert5, "ldap1.example.com", true },
+            { cert5, "ldap2.example.com", true },
+             /*
+              * cert6:
+              *   subject "cn=server,o=OpenDJ"
+              *   san [ dnsName "*.example.com" ] critical
+              */
+            { cert6, "ldap.example.com", true },
+            { cert6, "ldap10.example.com", true },
+            { cert6, "ldap.dev.example.com", false },
+            { cert6, "server", false },
+             /*
+              * cert7:
+              *   subject "cn=*.example.com,o=OpenDJ"
+              *   san (none)
+              */
+            { cert7, "ldap1.example.com", true },
+            { cert7, "ldap2.example.com", true },
+            { cert7, "ldap.dev.example.com", false },
+            { cert7, "192.168.0.1", false },
+            { cert7, "2001:db8::1:0:0:1", false },
+            { cert7, "ldap.example.org", false },
+             /*
+              * cert8:
+              *   subject "cn=server,o=OpenDJ"
+              *   san [ dnsName "ldap.example.com", ip "192.168.0.1" ] critical
+              */
+            { cert8, "ldap.example.com", true },
+            { cert8, "192.168.0.1", true },
+            { cert8, "ldap2.example.com", false },
+            { cert8, "192.168.0.2", false },
+            { cert8, "2001:db8::1:0:0:1", false },
+            { cert8, "server", false },
+             /*
+              * cert9:
+              *   subject "cn=server,o=OpenDJ"
+              *   san [ ip "2001:db8::1:0:0:1" ] critical
+              */
+            { cert9, "2001:db8::1:0:0:1", true },
+            { cert9, "ldap.example.com", false },
+            { cert9, "server", false },
+             /*
+              * cert10:
+              *   subject "cn=server,o=OpenDJ"
+              *   san [ email "info@forgerock.org" ] critical
+              */
+            { cert10, "ldap.example.com", false },
+            { cert10, "server", false },
+             /*
+              * cert11:
+              *   subject "cn=ldap.example.com,o=OpenDJ"
+              *   san [ uri "ldap://ldap.example.com ] critical
+              */
+            { cert11, "ldap.example.com", false },
+             /*
+              * cert12:
+              *   subject "cn=server,o=OpenDJ"
+              *   san [ dns "ldap.example.com", uri "ldap://ldap.example.com" ] non-critical
+              */
+            { cert12, "ldap.example.com", true },
+            { cert12, "server", true },
+             /*
+              * cert13:
+              *  subject ""
+              *  san [ dns "ldap.example.com" ] critical
+              */
+            { cert13, "ldap.example.com", true },
+            { cert13, "server", false },
+
+        };
+        // @formatter:on
+    }
+
+    /**
+     * Disables logging before the tests.
+     */
+    @BeforeClass
+    public void disableLogging() {
+        TestCaseUtils.setDefaultLogLevel(Level.SEVERE);
+    }
+
+    /**
+     * Re-enable logging after the tests.
+     */
+    @AfterClass
+    public void enableLogging() {
+        TestCaseUtils.setDefaultLogLevel(Level.INFO);
+    }
+
+    private X509Certificate getTestCertificate(String filename) throws Exception {
+        String path = TestCaseUtils.getTestFilePath("org.forgerock.opendj.ldap.TrustManagers" + File.separator
+                + filename);
+        CertificateFactory cf = CertificateFactory.getInstance("X.509");
+        try (InputStream is = new FileInputStream(path)) {
+            return (X509Certificate) cf.generateCertificate(is);
+        }
+    }
+
+    @Test(dataProvider = "hostnameTestData")
+    public void testHostnames(X509Certificate subject, String hostname, boolean expectedResult)
+            throws Exception {
+        X509TrustManager mgr = TrustManagers.checkHostName(hostname, TrustManagers.trustAll());
+        try {
+            mgr.checkServerTrusted(new X509Certificate[] { subject }, "RSA");
+            Assert.assertTrue(expectedResult);
+        } catch (CertificateException ce) {
+            Assert.assertFalse(expectedResult);
+        }
+    }
+}
diff --git a/opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert1.pem b/opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert1.pem
new file mode 100644
index 0000000..d7a53f1
--- /dev/null
+++ b/opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert1.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC1DCCAbygAwIBAgIEV3pVNjANBgkqhkiG9w0BAQsFADAsMQ8wDQYDVQQKDAZP
+cGVuREoxGTAXBgNVBAMMEGxkYXAuZXhhbXBsZS5jb20wHhcNMTYwNzA0MTIyMzM4
+WhcNMTcwNzA0MTIyMzM4WjAsMQ8wDQYDVQQKDAZPcGVuREoxGTAXBgNVBAMMEGxk
+YXAuZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCA
+Z4cWPDd5Usv6InfpewuYN2UQE20JvHmWT35OlObXxogk4SDncmuxqux8YozkA/5U
+Pa9hYFroVVpsa84x173tg01AsiBotxjBzdDfmue7NF/hDdlg6mYYuIDAi94f84Pi
+Y7z+/Sm0vFfKRZoRXJtmjnGZJBFzPi5L5xEzb94YvMhdqJPNRI2ihNSG4fGFRyq6
+1S4y2p8Hj6AeCwVjFVOjwtry4IG469EQbqrz5ZbPa4iJ0zNZNE51mGXDJA5B/ht9
+8DAGGae8rB3Eab5VGERjPJpyNNnU+irJVlr6Uef6iCguPoZyYc1LPOkaIynUey54
+RWLyPXvvxiXUIpNMotaTAgMBAAEwDQYJKoZIhvcNAQELBQADggEBABoPGkJAiEpg
+buPJsltgY4mfKLzRZw0RmTIh+zhm8aI06owAOtdN9x7tnh5bb7ZEPv3toP2AoCNl
+aWb2Y3EqpZvITXhDHw9F40UwW6sQapnlGjGZMgmttD2z/5ozeYhKWyNQgg/7nwMd
+atsZNmd4s/ePRPQzq2DoXQs+8Hp/jhvlVdU6/B8pUO4M6tT4gGGVHrB4cb0oBqW/
+xeoXYAhiHiYx6KNxXoRy6y9/TbIock0nd6BoXTepl4vziPVnHFW3Mw6CKUnzUC5A
+wKTmAR5Ja5Nu0grcJjrLvUDH6nSlVHR3xORH4Xbbqt8cUXgebWWiz6VoRnVa8iLs
+UbwVVuiYQrs=
+-----END CERTIFICATE-----
diff --git a/opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert10.pem b/opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert10.pem
new file mode 100644
index 0000000..bd350f1
--- /dev/null
+++ b/opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert10.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC5jCCAc6gAwIBAgIEV3pryzANBgkqhkiG9w0BAQsFADAiMQ8wDQYDVQQKDAZP
+cGVuREoxDzANBgNVBAMMBnNlcnZlcjAeFw0xNjA3MDQxNDAwMjVaFw0xNzA3MDQx
+NDAwMjVaMCIxDzANBgNVBAoMBk9wZW5ESjEPMA0GA1UEAwwGc2VydmVyMIIBIjAN
+BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlLXw1BOnRttU0MnIm56yBubRgyIp
+HqC2EZXwbqAb6WpFe72P7CsN1+UbpzfHJl6JV7bpKNbHXB1nA1e1W9zdTBy/j+GJ
+VYctZuXJQ453ehi8c3C8iv3SkFDgcw0+B8d3JWpFVTTaEMCJpwN/z06/3Kh9dDBM
+qelQqYOY2skRpMErJB7KJGqxhhoiBDl32b2hICszH0+637RlywfACe42CytBR2/i
+yzvRULN8fccGpHDvDg3XPZKP+wNcImJT70u0meCvN4HG3jdm1zakCQ5+6fHvzJp9
+rIzRZ6J7KDbC1oT/NUCWTeB+S2IexuS+8+KULNmWu2JQGyVEXTyI/7+/QQIDAQAB
+oyQwIjAgBgNVHREBAf8EFjAUgRJpbmZvQGZvcmdlcm9jay5vcmcwDQYJKoZIhvcN
+AQELBQADggEBAGrzyIPsp6fMHz3lfvY9uTO3+6J4FiXq5073WjdW100tO97lGvhW
+DyyCBDhDQHtvIqiNSkyE11vtuvVqxQAXujwsuzfSKhXZai7BSpfoz28QefV3Kn7F
++5gR+YG6e0xrBgsNO6ZupiHxGq0jocsge/HHRXYjZutqK+LUZFUBtAuaeJ6+Nqkw
+f8cMNWjQbqNKokqhK71NMnG4GLXg0/7lqlX8I0zs8jgKonlgsd1gQzWadqFSUjcW
+4g2xN8oHsCR4Z5K3hkqnrwuwNn2ca9byo3qU6vLRm/ShPphZ1I+Le9l9TZRNko33
+hccRusqQau4Rd2p0Un5cl5sSVS7Si0JUBZQ=
+-----END CERTIFICATE-----
diff --git a/opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert11.pem b/opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert11.pem
new file mode 100644
index 0000000..0c11a2d
--- /dev/null
+++ b/opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert11.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIC/zCCAeegAwIBAgIEV3qLJTANBgkqhkiG9w0BAQsFADAsMQ8wDQYDVQQKDAZP
+cGVuREoxGTAXBgNVBAMMEGxkYXAuZXhhbXBsZS5jb20wHhcNMTYwNzA0MTYxNDE3
+WhcNMTcwNzA0MTYxNDE3WjAsMQ8wDQYDVQQKDAZPcGVuREoxGTAXBgNVBAMMEGxk
+YXAuZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6
+EJt4dE7WU1Wi91Wvuy7fo33knGHJpPbLAmkBptRnJebKfpcz6Aiq9/P6ZTcPni/U
+2b8JF7PW3TioASx48ns3cpeLLSpG6oveWmf6SSjnrUn6yaOE607inULVCW8Slhrh
+5fU6gTppWqB3Ar2xeNLZI7HQBhoAKtFb4KNr5dhI++hMxA5W9u1rZ1xAMNavFn70
+XA/WBYM/DlAqSrbIICTNMuXdrYA6uvCv8ZM6U9Brms7qKDArLUUNDkRT64xJ3ajg
+AkaJ9KoPfRRx/T/VWDV7i3H5l+hrOBQ+tsc0I19XUvBam+BZFesQa6ImB555iTfb
+Ft9uBeoQ8ZyZGT2xdEshAgMBAAGjKTAnMCUGA1UdEQEB/wQbMBmGF2xkYXA6Ly9s
+ZGFwLmV4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQBw220f176kgZeDf3Pq
+/KIR0F32e4d1V1lgQmgqYkyuYWRTK2NLnDA1IytgCttdLHJgpOGT2FqqC3GDUiGY
+MOQBt3ktSoxFGm9aIgLvD4Vu2i/szKaA8WyVexExGLsuYCaRx8NVHPgpVf2xAuWV
+8qpkkAlyqk9epHrxsIKttu8H44DY/bxSbYsWOBMIXnLnZiKvD/UIzTyX2yg1mZsD
+UvF4Gpo0vgUMKE1qhLxh+9bgEoOMWb2BeTvdeJUimd7DdYC1sb7WvC+IeigACtVm
+/J19maobzW+GkHq79Oyx4i7cwTc5Fo6wYDIuAgN49NO9SzjVchtSI78VQ6DuyiER
+ztUD
+-----END CERTIFICATE-----
diff --git a/opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert12.pem b/opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert12.pem
new file mode 100644
index 0000000..3d2ca89
--- /dev/null
+++ b/opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert12.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC+jCCAeKgAwIBAgIEV3qL4jANBgkqhkiG9w0BAQsFADAiMQ8wDQYDVQQKDAZP
+cGVuREoxDzANBgNVBAMMBnNlcnZlcjAeFw0xNjA3MDQxNjE4MDdaFw0xNzA3MDQx
+NjE4MDdaMCIxDzANBgNVBAoMBk9wZW5ESjEPMA0GA1UEAwwGc2VydmVyMIIBIjAN
+BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAohD42B3LG+3SntrBn4a5rxcDfcTG
+HZadjScjwFZ4qosB0lGw+bO6UKLhHCKMbdoXMYqSWIxPiu/hySae8ecM7++PQsYN
+GZb4zn/cpJWJizlknhzhG4byUG+xsarGeV6/ayu8zLHMgelWWExvgeKmjeUCWgLs
+xDm23U7m34tbrN+nfyrx3yzc1gtczPehwd7Y3jY+JY4FiOCsHpaitqDKZM2nEg+K
+JuXjx0mhENh1qLOcUQax0AWCRh6B5nXCPSV469ed8zz9QyZfR7Lb5WcWOE95PSZM
+aEMJBR9vDZfxGjE2k4WipOCKhGjPBUsR4zazGvRfgNeyRsblCChE/enTgwIDAQAB
+ozgwNjA0BgNVHREELTArghBsZGFwLmV4YW1wbGUuY29thhdsZGFwOi8vbGRhcC5l
+eGFtcGxlLmNvbTANBgkqhkiG9w0BAQsFAAOCAQEAbvL8blsqkZ+qF8qM1o5G6cCY
+w49+4PQGh2mkseJokj7e4w7lfFk3o5jNMEWktig15qir2QWGSecHtBJajWTkw9Fe
+/OF74ptdKgBYKxsAF7IFQdozs+NUOc0q7tMp2L4g9rpOTk46EPRHT7zTo0sViNBR
+9/0vRsIsBx5HtC9h5f0cucF8ykB7Sibk6wCmubBZWbpm5Hk5aPfq4ui03/SAe79/
+e5Cu+1dBx6qJSA5XTG4D1IDizQVKRRTglVwV/jVuG49GDekIg/lVBYTDTD0Ld42+
+gCslJrRnw9QbbmejWyn2oZxIVwnCQwCzTZ4NunnOx4fD6OLzAMNDLMWloagAqw==
+-----END CERTIFICATE-----
diff --git a/opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert13.pem b/opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert13.pem
new file mode 100644
index 0000000..a750b79
--- /dev/null
+++ b/opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert13.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDLTCCAhWgAwIBAgIJALfTcCMjgUSOMA0GCSqGSIb3DQEBCwUAMD0xCzAJBgNV
+BAYTAkdCMQ0wCwYDVQQIDARBdm9uMRIwEAYDVQQKDAlGb3JnZVJvY2sxCzAJBgNV
+BAMMAkNBMB4XDTE2MDcwNjE0MzExMVoXDTE3MDcwNjE0MzExMVowADCCASIwDQYJ
+KoZIhvcNAQEBBQADggEPADCCAQoCggEBAN3Y2kkWv0wVNfhVowOCl+4crxsYRAaf
+RHPJERTfsLpAhdpHS4VdUTCcwfTmqGn3YQ427wnnl07DDN5YRokfRJArdnfifuoT
+/0UWa434AjK5CO9YiZ5BGt9bAoA4jdJqCR5pbW07dA0dAb5XEwirWWW5w/d4N3Eq
+26vRMXh7iDLYzuJ2rLDauq4gkdqvYwRi0WfuP7dl41nfxufGdt5vOMot/8akXO2t
+kqBJFCpdw5GKILQRo8sf4GaqTYdG0+eplO/rf+JOMWWO9bD6oEzxXk2gYyM5dvbd
+h/cv9AE+wBnYXvsW2tK0/emCt1dbVj8i4tPIRBD5IVW+SpO4MKo86AkCAwEAAaNt
+MGswCQYDVR0TBAIwADAdBgNVHQ4EFgQUD27k4aisMchWtHCnAFhHa03malYwHwYD
+VR0jBBgwFoAUl/cZwoNkFVFCD3lJ6lxuyNxMi0UwHgYDVR0RAQH/BBQwEoIQbGRh
+cC5leGFtcGxlLmNvbTANBgkqhkiG9w0BAQsFAAOCAQEAXj+Hx2EVswhqpdyW1jN0
+E3dxNDCeG3DHJsHmcbPMbTtpPT2xvT/6SNr8+FoLZAtw+ibDR+NFU1H7UGK+bpjh
+CfUkArmzbYun3FOkLYliMW/ffqr8lMpeqTuRkrOpAkqqo0dCrT2/ucraFkOhypfk
+scaeQq7pTLnjICZ8g9Yu2ru78q4nd5a7s5p/xQ1Om2oy/L+as4fcrNImHomvhfAg
+IPr0sX1G8ftlvRRfIzaZORqXB1raeLCXvVt6opyrxTyeWz8Lld3w+FkKwt3YXFA8
+Td6tR4JA9TTMlnwaR8dbBSfaPuVrVfoBh5KDOVXji/fER/gKUPaiH48h1rJVucF4
+Yg==
+-----END CERTIFICATE-----
diff --git a/opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert2.pem b/opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert2.pem
new file mode 100644
index 0000000..588fcc0
--- /dev/null
+++ b/opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert2.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDITCCAgmgAwIBAgIESjmh0DANBgkqhkiG9w0BAQsFADBBMQ8wDQYDVQQKEwZP
+cGVuREoxGTAXBgNVBAMTEGxkYXAuZXhhbXBsZS5jb20xEzARBgkqhkiG9w0BCQEW
+BGluZm8wHhcNMTYwNzA0MTMxMzIwWhcNMTYxMDAyMTMxMzIwWjBBMQ8wDQYDVQQK
+EwZPcGVuREoxGTAXBgNVBAMTEGxkYXAuZXhhbXBsZS5jb20xEzARBgkqhkiG9w0B
+CQEWBGluZm8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCjYZcndShB
+ByrBA80MRnW2RNMbGWCkxDZthpAFwsqQQPXxlKZ7gA/dbIG8ijQximW638mfjcu4
+YdViJ2XTafG0G0r1rKD+wl8IQPbWokMBSYWYrXcxN9KEediSL53CAyBqGHJ5TrZD
+S0mI5xiWWPxlSuCBdxCvAip8vOuFoLnkeiOdntGkG7lJDkT+3hPIJ0d/ol1ZB7VI
+YYkuxJcWZIQw/vhxYFsZ7aBhyxyyMqczhhMKJySwSr3o/Fe/HciLnALMDLXBrifz
+BOAjlp3uC6miUPSqUZJGNPR+eaMe7Tj6jTwa1C0EZ3sLmFCt+BUVr2+PrA4AoGDt
+9g7r3bkvIpyBAgMBAAGjITAfMB0GA1UdDgQWBBRQn86+hZEpcuM9o+xiDkQA0zHu
+dzANBgkqhkiG9w0BAQsFAAOCAQEAQxUaRbsig9JC0HaXlB1gAsy4NYBKMtcOrNhl
+eG0SCGGaW2SyWbLOgtfC/256J/CU42RhD6QpMuNQgq701NzkpxOl3rQC3jCLocge
+ECAKOe6T20pBhjY7ihgPyJFmsuGZe5bi2Qc0ZLsAe1rKn3t5Gas2WNrU6KwqmsbS
+QwKJJ9wAW4SP+GmHZ16Kz+xuZLxE5DY5dw4MOd25w4bagmMrr61JxXblTT76466q
+hfBtuEvxzSn2E7DGj7xP0h7X4nfixME8O+qnE+Xd9mIjZNhd5DkXjKy8ZsOzsdFR
+qLZmcpcyMFIgrN9/mjRvaNgf19B8lcgx9fM9frzIeOiv+lYFMw==
+-----END CERTIFICATE-----
diff --git a/opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert3.pem b/opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert3.pem
new file mode 100644
index 0000000..47117a2
--- /dev/null
+++ b/opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert3.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC+DCCAeCgAwIBAgIEV3pVWTANBgkqhkiG9w0BAQsFADAsMQ8wDQYDVQQKDAZP
+cGVuREoxGTAXBgNVBAMMEGxkYXAuZXhhbXBsZS5jb20wHhcNMTYwNzA0MTIyNDM1
+WhcNMTcwNzA0MTIyNDM1WjAsMQ8wDQYDVQQKDAZPcGVuREoxGTAXBgNVBAMMEGxk
+YXAuZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCI
++e9AysfcqtTpz78ULs4zs61UmxpflvysBKgc4Vo+fQFEfxLDb2q1i8Pf3Mv40Q9t
+9xCcSDYV8FFbKRinv15vVLZ/nHa1V7q2yqWuDF7P0pyEDjnVgmOOL+/VMHCz80GE
+UKpLvmHjOJoPJ1Xz+x5rhWFrOQAYrAsJBWEDJJ7kL9ixjajFK6z2iK7ufTvoks3B
+AYEYpPj8e7ngM4UpcPM9KsWU91QlV91Skfxyv5vH/JmdItYnGbv7o/6rTAdfkhZl
+6fS8ToK4rWxjl/W9Y9fQcPX1/jB1C5x8GQDFsiSzGosP4L6Rh8rwOArv2cDN/Igp
+T92Iv5fN1Sn/t4jWjVl5AgMBAAGjIjAgMB4GA1UdEQEB/wQUMBKCEGxkYXAuZXhh
+bXBsZS5vcmcwDQYJKoZIhvcNAQELBQADggEBADKziDxHdbMLfAglpGxpHvU68/9F
+yqUSFLamRxAYM+bnxskVf6vJG4Htq0XHrNOGBTpEIacd9txMJkyuqaTcNE9xQmPS
+xMnv5nTQumq65AD3MJvXBFpG+kumVv5PKvHVIfWykAatvQ2M7w/dDwvjLkB6LdrY
+Y1sW3fyri9GhDzXR996Cdlfx+MOpB9f/hmDJzzCbYEwopVJ1tTl0JrqR8Rwih2MS
+Kiv7ECkubGnphgwN6+ztLepmbDG2sCevWNAewp49NeCiZBwg3uhpvuxqjFn8zzkB
+VhuXCNfVkXZIv3tVnr8cdyKqIG/WOchhho00gfdI7S76aOlF6kvCAUPEP5k=
+-----END CERTIFICATE-----
diff --git a/opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert4.pem b/opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert4.pem
new file mode 100644
index 0000000..289d410
--- /dev/null
+++ b/opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert4.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC9TCCAd2gAwIBAgIEV3pdzTANBgkqhkiG9w0BAQsFADAsMQ8wDQYDVQQKDAZP
+cGVuREoxGTAXBgNVBAMMEGxkYXAuZXhhbXBsZS5jb20wHhcNMTYwNzA0MTMwMDMw
+WhcNMTcwNzA0MTMwMDMwWjAsMQ8wDQYDVQQKDAZPcGVuREoxGTAXBgNVBAMMEGxk
+YXAuZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCP
+Dbagq6CxseV2HqrjKjj2Er/qsDqk9CVncEZsJL6kV2/S8Umj6w/vOxtsw06DiAV6
+Yvs64JDptSPg2ip0pI8CxqO2b7DOA7qwwp8ng0Uw4U6WYGn4tt5zFC/F9hORwFTC
+uN3Qm2k+hwCQdIJq5yTEAoq8qNJvUUqCJ7bCcJbNp4qQuFh7gUcqjAAYjc42ABOw
+C4NVnfqpHnFa0GibIGZZgQaPWatCWC/+KScJZavH+I6I+kNHqP+0XEvkFYBp/BM/
+s8Bg+2rNrrsK1ikq4oS9zOfjcOU2a+1CDKidazxdVYc0Y1p326o74bVYE0vcPE1e
+cgb8Sc91wgXml7DDbN9PAgMBAAGjHzAdMBsGA1UdEQQUMBKCEGxkYXAuZXhhbXBs
+ZS5vcmcwDQYJKoZIhvcNAQELBQADggEBACio2XKfLsmOH3e1uYhBiqE9+CbuQz6u
+nyMO2BaRK69rg1703AI+G6tYf9rC32Lqmt9wGd/IOHcucytPXcq9LQE99f5CP8Pt
+D82mBjhbNA4fb/Xf++DprfmywTqU92U5uMUyaGw0DN7v4qqRUeG45O5Mbi/7kAqb
+cxYlxwVE5wy816X8wDCa1PvkOFvVO8pVdzhxx7L0jRhEAuqEyCpvlZbDHpvF/K+6
+vskoLM42ctrUBi+rFj/5oHw/qw7jhEulMJoQ5sDXxr9+eMbvzszeVJ695ILnBPDQ
+7J7Dmodsk2LMF6r1AJX+f7jmOjGn9uw2upSRTkdI+TIAZKhvGjiQw00=
+-----END CERTIFICATE-----
diff --git a/opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert5.pem b/opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert5.pem
new file mode 100644
index 0000000..91f45eb
--- /dev/null
+++ b/opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert5.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC+DCCAeCgAwIBAgIEV3peTjANBgkqhkiG9w0BAQsFADAiMQ8wDQYDVQQKDAZP
+cGVuREoxDzANBgNVBAMMBnNlcnZlcjAeFw0xNjA3MDQxMzAyNTNaFw0xNzA3MDQx
+MzAyNTNaMCIxDzANBgNVBAoMBk9wZW5ESjEPMA0GA1UEAwwGc2VydmVyMIIBIjAN
+BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApxPO9rpEUKWOGJmOW6lqPEkYR8C6
+C/NCB+qR7+oPYRMyllaAN8zqc2FZ1gKeDkv4QmI0T8Eg+9tHbRpjkc72tE9nkPFw
+IWjhUpfxcbdBmQST7gPlEYj/AVdmd1m25BOJg9YNuMYftyHTRli1ULYOz7AWwTpi
+w8v9uT7q2dwMxkbkECa5Y5E0zyuIadlvZKz1GnH1O6/5PKSgMk9qP6X3Bx+QYyq2
+X3SfPSrJ5V16h/mIaVgz17OIm8aVOAPaC+b/a9PsIWBF1IapQeBodLc9E+1uvjjI
+AyyBibKJreditG8LRiAr+0bqMPrtYm5X7vKtq6Q+l0ryB5T+V8x92eBrAwIDAQAB
+ozYwNDAyBgNVHREBAf8EKDAmghFsZGFwMS5leGFtcGxlLmNvbYIRbGRhcDIuZXhh
+bXBsZS5jb20wDQYJKoZIhvcNAQELBQADggEBAABh4JgGss1aZ3qc6SmkvYSalmFp
+rQJahuwzGBaxDiAKpkfhPRy7g09xzHyWy/3zrYXTURwqqJnWujPfv4h/tJQt3ptl
+MW9AzmAYVVBY4u14TdJ/zl67iRukS6SKtN94r67e+lwECxWCD/jheJVbl5/mGcvO
+O6uGTc/cXahpwb31Osv8qGQyJLi+rc6x7vMMG3tvAss45RmSkWiKzmITNa9eWqOS
+EOercFRlLdaTQ/0FpnMpsO0Ejl1w71I4XXeZ7Ay2fHclBm6S016nMgWqUU9QziFG
+wc1mrBP6RKmP+1v0sCO8gXlbzuhnqDSDTBlw4xGZ++Ej3mpIxaX9ZOkqz4k=
+-----END CERTIFICATE-----
diff --git a/opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert6.pem b/opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert6.pem
new file mode 100644
index 0000000..d7346cd
--- /dev/null
+++ b/opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert6.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC4TCCAcmgAwIBAgIEV3peuzANBgkqhkiG9w0BAQsFADAiMQ8wDQYDVQQKDAZP
+cGVuREoxDzANBgNVBAMMBnNlcnZlcjAeFw0xNjA3MDQxMzA0MjJaFw0xNzA3MDQx
+MzA0MjJaMCIxDzANBgNVBAoMBk9wZW5ESjEPMA0GA1UEAwwGc2VydmVyMIIBIjAN
+BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAg5KTCDHu3LhIF/9XlRaVvgFFjfc4
+D+npzCFYLujbNmY1G/I9s5o7Be0/9W/1OPZjGO4Wb8fAMfV1qgMGE4XR40+UGtmx
+MkBPmVPSV8mgCrGRPYGXkSgvJv8X3lVf3ldbqJOKcaF70AwLrATlqNbCyWweoby8
+PSzjlDSDGTUAa1XTj7ACgdcrqq6OdL8N2sqM4C+vnC7GAaqdCq/wQaPpshC6c3gB
+lo9+t2hqy3ob/ltnsRA4hvmyB5JJ9LNsfmvhzIGpX/fX1bc13+Katfk9sXIEcn0A
+zTUKY9vVppsxpSbhaaM2x0neBhcJDT/BnGBA7NO/42uc8njciyqhuevOUwIDAQAB
+ox8wHTAbBgNVHREBAf8EETAPgg0qLmV4YW1wbGUuY29tMA0GCSqGSIb3DQEBCwUA
+A4IBAQB3wyyQjHIU2YCTG59d8C1D8Y6Mb5loodCYkTFqZU4HPaGi8qsBLdzeYvkZ
+ntaVogm0jRGAgWlgKwo2IS5a9smctWGK65bLb1Ts3h65Ft0mhMZk4MNUli/yKM+v
+zQqRymRRHgj50juaJYiKLWICH6l66AwUPnuZ2pGLDUSxdWTRvmVsfzamySOba+yz
+u577MQMXXwybZVhWs6ZZbJ5yVqkXEkFzKgBRPOI5y+reeLRqEfWEb0J+N0h3rYIE
+lj01B7EdUQBqle0slB/TUqZkFH+WOv5DX7TwRHOb/qAgIl9EraLxOM/UI/UVUKA6
+97y8hmW/nDrEz1rqEYDr/Fj4/s5l
+-----END CERTIFICATE-----
diff --git a/opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert7.pem b/opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert7.pem
new file mode 100644
index 0000000..b5dd8f9
--- /dev/null
+++ b/opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert7.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIICzjCCAbagAwIBAgIEV3pfHDANBgkqhkiG9w0BAQsFADApMQ8wDQYDVQQKDAZP
+cGVuREoxFjAUBgNVBAMMDSouZXhhbXBsZS5jb20wHhcNMTYwNzA0MTMwNTQ3WhcN
+MTcwNzA0MTMwNTQ3WjApMQ8wDQYDVQQKDAZPcGVuREoxFjAUBgNVBAMMDSouZXhh
+bXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCLrepSmClz
+yVV5IYWQ8DGq3K4VgfithXm6pY4ruoH5nwfJ3NbOQukyOOJ8VLvPvbSGMFDeSvtp
+dzKfNeYB06mVV7ASvOJZPzD+Teemr6p2+sT/KosstxkrtgWJHRsyVajyMIYNtLh9
+2EpLBGYdM4LpzVWmOjNep7it3zH7xqL6IGesI7DVKfv2dQy0RnMV2+B2etDpE5Gi
+duxnVHUxBUrC1FxbIvWoAG0H1ijgP2YqGlZCaOGRsBSajGs1Vfhz8hTarzTY/Bgz
+66ZDB8t9PM2L7LN73S9QRSGiblloBNLPdbE+cus5t46mNgXswnCv4C9zHqtY+yPf
+MYYzSDzJm1dbAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAAgS3k/+GL7JC20KYzkk
+jvhDjRCT8xmNNHOsJEPBjGDmj+24s4TwqQdJJ6GmyNM5h5bCPbDaUBExZaagTurl
+LLTbqT0UhnlICsFye8/CqP1H9zRneZxVE9U6XIZQCvuhUcvxaGUqYFwa8Fj31R0i
+tQQO0WaghxduMDaXb/WVCA94PuTpuPtzLcchdTogO+2nPOvG7AG0YeWhvWkpn/Go
+a9FoCx0+BM37UV0eG6rqmrt8dQTx/IdoickNfCuu4qQFzvM2ct0SJrmb4VWMPP2J
+QemHQ1nPRFtZenPEc29bSsYbhmauX5Z4N4+soAzGR0Q5By4/11On+w2oAGcz31eg
+NuA=
+-----END CERTIFICATE-----
diff --git a/opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert8.pem b/opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert8.pem
new file mode 100644
index 0000000..f0ffa22
--- /dev/null
+++ b/opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert8.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC6jCCAdKgAwIBAgIEV3pfizANBgkqhkiG9w0BAQsFADAiMQ8wDQYDVQQKDAZP
+cGVuREoxDzANBgNVBAMMBnNlcnZlcjAeFw0xNjA3MDQxMzA4MDNaFw0xNzA3MDQx
+MzA4MDNaMCIxDzANBgNVBAoMBk9wZW5ESjEPMA0GA1UEAwwGc2VydmVyMIIBIjAN
+BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqceuUbp2niP3eAla+fMlzX+Kt/zA
+Xfo/UiY/C+qdcryvuINm4d37+EVW8PUTr9uBKUvsWBUHJ36r7LGCSDlU+3aS+/Du
+OFBArKwfayAb8oLwcbWbFRuG406yqBAIIOTFpON/5PATZ1oljyYGEUbPMjPCN5K4
+pBSIbLU/yugWPWg0Kx1VtwrRIiRydSu5QVvGA8vDX4+IsDH2UQSgzz8suRLQvbKk
+4NroCEzjsmRUQhs0z4vuVPM+4ybMvD13IIrL18SCr8b1drMgeBhXtcD1Z62v/tm0
++qFq3JLc+9Gsf2uJACFOtTwuI8LkUAODbVy7UWL1vW3X02GXq27JXYTZfQIDAQAB
+oygwJjAkBgNVHREBAf8EGjAYghBsZGFwLmV4YW1wbGUuY29thwTAqAABMA0GCSqG
+SIb3DQEBCwUAA4IBAQAYdVC67IoywXFAUqsCVh2gxSA7Sfu7MCcccJyjG+XxkJYx
+S6xuEbvLf8IdG1/VRpO6V07AHSsUz/O2XOwNudfyb59Syvz+Y/OTZaw0w68N+eZp
+/F5RWrfzY6anDRRcRDy/uG5PAz7IEdgYyfwCITIOZimHzH1GkBfuGgHSRDktH4q7
+pvRrxqJ5S4NDqcUYu0KWvTbkFZeh+a0h5FRdlCW2B9HhXkb6OpoZmHVKpwfOanPQ
+x4ztQkGgGJpSHv5cqFNvmpTFxA07ZXFXBWnRQS59pjOgbLiLUxvTyY4a0yfKDIrU
+7yj7TEow4LBs2r1zXPVuEIs1bt+4W1pmyUHf463V
+-----END CERTIFICATE-----
diff --git a/opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert9.pem b/opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert9.pem
new file mode 100644
index 0000000..a93c423
--- /dev/null
+++ b/opendj-core/src/test/resources/org.forgerock.opendj.ldap.TrustManagers/cert9.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDAzCCAeugAwIBAgIEJkgy6zANBgkqhkiG9w0BAQsFADAiMQ8wDQYDVQQKEwZP
+cGVuREoxDzANBgNVBAMTBnNlcnZlcjAeFw0xNjA3MDQxMzQ3MTNaFw0xNjEwMDIx
+MzQ3MTNaMCIxDzANBgNVBAoTBk9wZW5ESjEPMA0GA1UEAxMGc2VydmVyMIIBIjAN
+BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAl63LuujQxWqIy7DiDsEDIKDuSTrx
+Bw3F0qsn5XoIpXpTqye9z5Ux/CA4GzSTJFiTEGnThqBQUiegz8XHxQRXg0WPddsB
+tQVrxRZ1SGnpv0rqX0izdz8Wl1kN6cczzyU06A4Fyx5uDUBfa6vQcLzcRM87Ycsn
+cENnB2JUA0G/RRP7dxrpMtln7+iEFr3thAHdmbXodJ9WTpi3tXSYzzPaSLWAbycc
+dooMdiQi0rdbYhz6s4FWN22w2i39rIuQKNiiDntJSW+psFXEKCa4exAktEM3vZ/V
+WKZGQasN2Ja2BNf9BSq8c1LGuBlPh1eCjFhxKLrdZl7dfxr7Snk8TLZbjwIDAQAB
+o0EwPzAeBgNVHREBAf8EFDAShxAgAQ24AAAAAAABAAAAAAABMB0GA1UdDgQWBBQ7
+y7Tq2SHqsFQGFA5QjvLJ3hkj3TANBgkqhkiG9w0BAQsFAAOCAQEAbZtb1IMVogE/
+ZJcdgRked8pknyhAun2xtaL6ob3PEWHFqeK8WThbvRO5j8FWvcVYMX9OXOJCQJVu
+iAvRxZQcRvbu2ApefrvoYQKYFr5Wr9cf8lKe3ggbeqcj48SNNOF/WMq+q4Yv9C8G
+cBlxJQvRR4lubXd3FC/xM+YElPJxldMqSZsz4TvtEJYd+lfI3fR0PW8D7svNdUEp
+7osvwsA5re2/s9/yyVM/dNUr2ud1lbnA0U5ECRyIHn0V32E0Qmg+yk9rayu1mmbp
+Y2M8f3DXVm8tLlpSyV4qKnotgQ09Ip5sQAcKB2k8fuDkjtxJKf/lo4EuXim4skYZ
+xP+LN/FvNw==
+-----END CERTIFICATE-----

--
Gitblit v1.10.0