001/* 002 * Copyright 2012-2018 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2012-2018 Ping Identity Corporation 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<>(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 @Override() 180 public void checkClientTrusted(final X509Certificate[] chain, 181 final String authType) 182 throws CertificateException 183 { 184 final StringBuilder buffer = new StringBuilder(); 185 for (final String s : acceptableHostNames) 186 { 187 buffer.setLength(0); 188 if (HostNameSSLSocketVerifier.certificateIncludesHostname(s, chain[0], 189 allowWildcards, buffer)) 190 { 191 return; 192 } 193 } 194 195 throw new CertificateException( 196 ERR_HOSTNAME_NOT_FOUND.get(buffer.toString())); 197 } 198 199 200 201 /** 202 * Checks to determine whether the provided server certificate chain should be 203 * trusted. 204 * 205 * @param chain The server certificate chain for which to make the 206 * determination. 207 * @param authType The key exchange algorithm used. 208 * 209 * @throws CertificateException If the provided server certificate chain 210 * should not be trusted. 211 */ 212 @Override() 213 public void checkServerTrusted(final X509Certificate[] chain, 214 final String authType) 215 throws CertificateException 216 { 217 final StringBuilder buffer = new StringBuilder(); 218 for (final String s : acceptableHostNames) 219 { 220 buffer.setLength(0); 221 if (HostNameSSLSocketVerifier.certificateIncludesHostname(s, chain[0], 222 allowWildcards, buffer)) 223 { 224 return; 225 } 226 } 227 228 throw new CertificateException( 229 ERR_HOSTNAME_NOT_FOUND.get(buffer.toString())); 230 } 231 232 233 234 /** 235 * Retrieves the accepted issuer certificates for this trust manager. This 236 * will always return an empty array. 237 * 238 * @return The accepted issuer certificates for this trust manager. 239 */ 240 @Override() 241 public X509Certificate[] getAcceptedIssuers() 242 { 243 return NO_CERTIFICATES; 244 } 245}