001    /*
002     * Copyright 2012-2015 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2012-2015 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    }