001 /*
002 * Copyright 2012-2016 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2012-2016 UnboundID Corp.
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021 package com.unboundid.util.ssl;
022
023
024
025 import java.security.cert.CertificateException;
026 import java.security.cert.X509Certificate;
027 import java.util.Collection;
028 import java.util.Collections;
029 import java.util.LinkedHashSet;
030 import java.util.Set;
031 import javax.net.ssl.X509TrustManager;
032
033 import com.unboundid.util.NotMutable;
034 import com.unboundid.util.StaticUtils;
035 import com.unboundid.util.ThreadSafety;
036 import com.unboundid.util.ThreadSafetyLevel;
037 import com.unboundid.util.Validator;
038
039 import static com.unboundid.util.ssl.SSLMessages.*;
040
041
042
043 /**
044 * This class provides an SSL trust manager that will only accept certificates
045 * whose hostname (as contained in the CN subject attribute or a subjectAltName
046 * extension) matches an expected value. Only the dNSName, iPAddress, and
047 * uniformResourceIdentifier subjectAltName formats are supported.
048 * <BR><BR>
049 * This implementation optionally supports wildcard certificates, which have a
050 * hostname that starts with an asterisk followed by a period and domain or
051 * subdomain. For example, "*.example.com" could be considered a match for
052 * anything in the "example.com" domain. If wildcards are allowed, then only
053 * the CN subject attribute and dNSName subjectAltName extension will be
054 * examined, and only the leftmost element of a hostname may be a wildcard
055 * character.
056 * <BR><BR>
057 * Note that no other elements of the certificate are examined, so it is
058 * strongly recommended that this trust manager be used in an
059 * {@link AggregateTrustManager} in conjunction with other trust managers that
060 * perform other forms of validation.
061 */
062 @NotMutable()
063 @ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
064 public final class HostNameTrustManager
065 implements X509TrustManager
066 {
067 /**
068 * A pre-allocated empty certificate array.
069 */
070 private static final X509Certificate[] NO_CERTIFICATES =
071 new X509Certificate[0];
072
073
074
075 // Indicates whether to allow wildcard certificates (which
076 private final boolean allowWildcards;
077
078 // The set of hostname values that will be considered acceptable.
079 private final Set<String> acceptableHostNames;
080
081
082
083 /**
084 * Creates a new hostname trust manager with the provided information.
085 *
086 * @param allowWildcards Indicates whether to allow wildcard
087 * certificates which contain an asterisk as the
088 * first component of a CN subject attribute or
089 * dNSName subjectAltName extension.
090 * @param acceptableHostNames The set of hostnames and/or IP addresses that
091 * will be considered acceptable. Only
092 * certificates with a CN or subjectAltName value
093 * that exactly matches one of these names
094 * (ignoring differences in capitalization) will
095 * be considered acceptable. It must not be
096 * {@code null} or empty.
097 */
098 public HostNameTrustManager(final boolean allowWildcards,
099 final String... acceptableHostNames)
100 {
101 this(allowWildcards, StaticUtils.toList(acceptableHostNames));
102 }
103
104
105
106 /**
107 * Creates a new hostname trust manager with the provided information.
108 *
109 * @param allowWildcards Indicates whether to allow wildcard
110 * certificates which contain an asterisk as the
111 * first component of a CN subject attribute or
112 * dNSName subjectAltName extension.
113 * @param acceptableHostNames The set of hostnames and/or IP addresses that
114 * will be considered acceptable. Only
115 * certificates with a CN or subjectAltName value
116 * that exactly matches one of these names
117 * (ignoring differences in capitalization) will
118 * be considered acceptable. It must not be
119 * {@code null} or empty.
120 */
121 public HostNameTrustManager(final boolean allowWildcards,
122 final Collection<String> acceptableHostNames)
123 {
124 Validator.ensureNotNull(acceptableHostNames);
125 Validator.ensureFalse(acceptableHostNames.isEmpty(),
126 "The set of acceptable host names must not be empty.");
127
128 this.allowWildcards = allowWildcards;
129
130 final LinkedHashSet<String> nameSet =
131 new LinkedHashSet<String>(acceptableHostNames.size());
132 for (final String s : acceptableHostNames)
133 {
134 nameSet.add(StaticUtils.toLowerCase(s));
135 }
136
137 this.acceptableHostNames = Collections.unmodifiableSet(nameSet);
138 }
139
140
141
142 /**
143 * Indicates whether wildcard certificates should be allowed, which may
144 * match multiple hosts in a given domain or subdomain.
145 *
146 * @return {@code true} if wildcard certificates should be allowed, or
147 * {@code false} if not.
148 */
149 public boolean allowWildcards()
150 {
151 return allowWildcards;
152 }
153
154
155
156 /**
157 * Retrieves the set of hostnames that will be considered acceptable.
158 *
159 * @return The set of hostnames that will be considered acceptable.
160 */
161 public Set<String> getAcceptableHostNames()
162 {
163 return acceptableHostNames;
164 }
165
166
167
168 /**
169 * Checks to determine whether the provided client certificate chain should be
170 * trusted.
171 *
172 * @param chain The client certificate chain for which to make the
173 * determination.
174 * @param authType The authentication type based on the client certificate.
175 *
176 * @throws CertificateException If the provided client certificate chain
177 * should not be trusted.
178 */
179 public void checkClientTrusted(final X509Certificate[] chain,
180 final String authType)
181 throws CertificateException
182 {
183 final StringBuilder buffer = new StringBuilder();
184 for (final String s : acceptableHostNames)
185 {
186 buffer.setLength(0);
187 if (HostNameSSLSocketVerifier.certificateIncludesHostname(s, chain[0],
188 allowWildcards, buffer))
189 {
190 return;
191 }
192 }
193
194 throw new CertificateException(
195 ERR_HOSTNAME_NOT_FOUND.get(buffer.toString()));
196 }
197
198
199
200 /**
201 * Checks to determine whether the provided server certificate chain should be
202 * trusted.
203 *
204 * @param chain The server certificate chain for which to make the
205 * determination.
206 * @param authType The key exchange algorithm used.
207 *
208 * @throws CertificateException If the provided server certificate chain
209 * should not be trusted.
210 */
211 public void checkServerTrusted(final X509Certificate[] chain,
212 final String authType)
213 throws CertificateException
214 {
215 final StringBuilder buffer = new StringBuilder();
216 for (final String s : acceptableHostNames)
217 {
218 buffer.setLength(0);
219 if (HostNameSSLSocketVerifier.certificateIncludesHostname(s, chain[0],
220 allowWildcards, buffer))
221 {
222 return;
223 }
224 }
225
226 throw new CertificateException(
227 ERR_HOSTNAME_NOT_FOUND.get(buffer.toString()));
228 }
229
230
231
232 /**
233 * Retrieves the accepted issuer certificates for this trust manager. This
234 * will always return an empty array.
235 *
236 * @return The accepted issuer certificates for this trust manager.
237 */
238 public X509Certificate[] getAcceptedIssuers()
239 {
240 return NO_CERTIFICATES;
241 }
242 }