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    }