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.ArrayList; 028import java.util.Collection; 029import java.util.Collections; 030import java.util.List; 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.Debug.*; 040import static com.unboundid.util.ssl.SSLMessages.*; 041 042 043 044/** 045 * This class provides an SSL trust manager that has the ability to delegate the 046 * determination about whether to trust a given certificate to one or more other 047 * trust managers. It can be configured to use a logical AND (i.e., all 048 * associated trust managers must be satisfied) or a logical OR (i.e., at least 049 * one of the associated trust managers must be satisfied). 050 */ 051@NotMutable() 052@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 053public final class AggregateTrustManager 054 implements X509TrustManager 055{ 056 /** 057 * A pre-allocated empty certificate array. 058 */ 059 private static final X509Certificate[] NO_CERTIFICATES = 060 new X509Certificate[0]; 061 062 063 064 // Indicates whether to require all of the associated trust managers to accept 065 // a presented certificate, or just to require at least one of them to accept 066 // the certificate. 067 private final boolean requireAllAccepted; 068 069 // The trust managers that will be used to ultimately make the determination. 070 private final List<X509TrustManager> trustManagers; 071 072 073 074 /** 075 * Creates a new aggregate trust manager with the provided information. 076 * 077 * @param requireAllAccepted Indicates whether all of the associated trust 078 * managers must accept a presented certificate 079 * for it to be allowed, or just at least one of 080 * them. 081 * @param trustManagers The set of trust managers to use to make the 082 * determination. It must not be {@code null} or 083 * empty. 084 */ 085 public AggregateTrustManager(final boolean requireAllAccepted, 086 final X509TrustManager ... trustManagers) 087 { 088 this(requireAllAccepted, StaticUtils.toList(trustManagers)); 089 } 090 091 092 093 /** 094 * Creates a new aggregate trust manager with the provided information. 095 * 096 * @param requireAllAccepted Indicates whether all of the associated trust 097 * managers must accept a presented certificate 098 * for it to be allowed, or just at least one of 099 * them. 100 * @param trustManagers The set of trust managers to use to make the 101 * determination. It must not be {@code null} or 102 * empty. 103 */ 104 public AggregateTrustManager(final boolean requireAllAccepted, 105 final Collection<X509TrustManager > trustManagers) 106 { 107 Validator.ensureNotNull(trustManagers); 108 Validator.ensureFalse(trustManagers.isEmpty(), 109 "The set of associated trust managers must not be empty."); 110 111 this.requireAllAccepted = requireAllAccepted; 112 this.trustManagers = Collections.unmodifiableList( 113 new ArrayList<X509TrustManager>(trustManagers)); 114 } 115 116 117 118 /** 119 * Indicates whether all of the associated trust managers will be required to 120 * accept a given certificate for it to be considered acceptable. 121 * 122 * @return {@code true} if all of the associated trust managers will be 123 * required to accept the provided certificate chain, or 124 * {@code false} if it will be acceptable for at least one trust 125 * manager to accept the chain even if one or more others do not. 126 */ 127 public boolean requireAllAccepted() 128 { 129 return requireAllAccepted; 130 } 131 132 133 134 /** 135 * Retrieves the set of trust managers that will be used to perform the 136 * validation. 137 * 138 * @return The set of trust managers that will be used to perform the 139 * validation. 140 */ 141 public List<X509TrustManager> getAssociatedTrustManagers() 142 { 143 return trustManagers; 144 } 145 146 147 148 /** 149 * Checks to determine whether the provided client certificate chain should be 150 * trusted. 151 * 152 * @param chain The client certificate chain for which to make the 153 * determination. 154 * @param authType The authentication type based on the client certificate. 155 * 156 * @throws CertificateException If the provided client certificate chain 157 * should not be trusted. 158 */ 159 public void checkClientTrusted(final X509Certificate[] chain, 160 final String authType) 161 throws CertificateException 162 { 163 ArrayList<String> exceptionMessages = null; 164 165 for (final X509TrustManager m : trustManagers) 166 { 167 try 168 { 169 m.checkClientTrusted(chain, authType); 170 171 if (! requireAllAccepted) 172 { 173 return; 174 } 175 } 176 catch (final CertificateException ce) 177 { 178 debugException(ce); 179 180 if (requireAllAccepted) 181 { 182 throw ce; 183 } 184 else 185 { 186 if (exceptionMessages == null) 187 { 188 exceptionMessages = new ArrayList<String>(trustManagers.size()); 189 } 190 191 exceptionMessages.add(ce.getMessage()); 192 } 193 } 194 } 195 196 // If we've gotten here and there are one or more exception messages, then 197 // it means that none of the associated trust managers accepted the 198 // certificate. 199 if ((exceptionMessages != null) && (! exceptionMessages.isEmpty())) 200 { 201 if (exceptionMessages.size() == 1) 202 { 203 throw new CertificateException(exceptionMessages.get(0)); 204 } 205 else 206 { 207 throw new CertificateException( 208 ERR_AGGREGATE_TRUST_MANAGER_NONE_TRUSTED.get( 209 SSLUtil.certificateToString(chain[0]), 210 StaticUtils.concatenateStrings(exceptionMessages))); 211 } 212 } 213 } 214 215 216 217 /** 218 * Checks to determine whether the provided server certificate chain should be 219 * trusted. 220 * 221 * @param chain The server certificate chain for which to make the 222 * determination. 223 * @param authType The key exchange algorithm used. 224 * 225 * @throws CertificateException If the provided server certificate chain 226 * should not be trusted. 227 */ 228 public void checkServerTrusted(final X509Certificate[] chain, 229 final String authType) 230 throws CertificateException 231 { 232 ArrayList<String> exceptionMessages = null; 233 234 for (final X509TrustManager m : trustManagers) 235 { 236 try 237 { 238 m.checkServerTrusted(chain, authType); 239 240 if (! requireAllAccepted) 241 { 242 return; 243 } 244 } 245 catch (final CertificateException ce) 246 { 247 debugException(ce); 248 249 if (requireAllAccepted) 250 { 251 throw ce; 252 } 253 else 254 { 255 if (exceptionMessages == null) 256 { 257 exceptionMessages = new ArrayList<String>(trustManagers.size()); 258 } 259 260 exceptionMessages.add(ce.getMessage()); 261 } 262 } 263 } 264 265 // If we've gotten here and there are one or more exception messages, then 266 // it means that none of the associated trust managers accepted the 267 // certificate. 268 if ((exceptionMessages != null) && (! exceptionMessages.isEmpty())) 269 { 270 if (exceptionMessages.size() == 1) 271 { 272 throw new CertificateException(exceptionMessages.get(0)); 273 } 274 else 275 { 276 throw new CertificateException( 277 ERR_AGGREGATE_TRUST_MANAGER_NONE_TRUSTED.get( 278 SSLUtil.certificateToString(chain[0]), 279 StaticUtils.concatenateStrings(exceptionMessages))); 280 } 281 } 282 } 283 284 285 286 /** 287 * Retrieves the accepted issuer certificates for this trust manager. This 288 * will always return an empty array. 289 * 290 * @return The accepted issuer certificates for this trust manager. 291 */ 292 public X509Certificate[] getAcceptedIssuers() 293 { 294 return NO_CERTIFICATES; 295 } 296}