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 */
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
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)
052 public 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 }