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