001/* 002 * Copyright 2012-2017 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2012-2017 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 */ 021package com.unboundid.util.ssl; 022 023 024 025import java.security.cert.CertificateException; 026import java.security.cert.X509Certificate; 027import java.util.Collection; 028import java.util.Collections; 029import java.util.LinkedHashSet; 030import java.util.Set; 031import javax.net.ssl.X509TrustManager; 032 033import com.unboundid.util.NotMutable; 034import com.unboundid.util.StaticUtils; 035import com.unboundid.util.ThreadSafety; 036import com.unboundid.util.ThreadSafetyLevel; 037import com.unboundid.util.Validator; 038 039import 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) 064public 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}