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.ArrayList;
028 import java.util.Collection;
029 import java.util.Collections;
030 import java.util.List;
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.Debug.*;
040 import 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)
053 public 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 }