001    /*
002     * Copyright 2008-2015 UnboundID Corp.
003     * All Rights Reserved.
004     */
005    /*
006     * Copyright (C) 2008-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.io.File;
026    import java.io.FileInputStream;
027    import java.io.Serializable;
028    import java.security.KeyStore;
029    import java.security.cert.CertificateException;
030    import java.security.cert.X509Certificate;
031    import java.util.Date;
032    import javax.net.ssl.TrustManager;
033    import javax.net.ssl.TrustManagerFactory;
034    import javax.net.ssl.X509TrustManager;
035    
036    
037    import static com.unboundid.util.Debug.*;
038    import static com.unboundid.util.Validator.*;
039    import static com.unboundid.util.ssl.SSLMessages.*;
040    
041    
042    
043    /**
044     * This class provides an SSL trust manager that will consult a specified trust
045     * store file to determine whether to trust a certificate that is presented to
046     * it.  By default, it will use the default trust store format for the JVM
047     * (e.g., "JKS" for Sun-provided Java implementations), but alternate formats
048     * like PKCS12 may be used.
049     */
050    public final class TrustStoreTrustManager
051           implements X509TrustManager, Serializable
052    {
053      /**
054       * The serial version UID for this serializable class.
055       */
056      private static final long serialVersionUID = -4093869102727719415L;
057    
058    
059    
060      // Indicates whether to automatically trust expired or not-yet-valid
061      // certificates.
062      private final boolean examineValidityDates;
063    
064      // The PIN to use to access the trust store.
065      private final char[] trustStorePIN;
066    
067      // The path to the trust store file.
068      private final String trustStoreFile;
069    
070      // The format to use for the trust store file.
071      private final String trustStoreFormat;
072    
073    
074    
075      /**
076       * Creates a new instance of this trust store trust manager that will trust
077       * all certificates in the specified file within the validity window. It will
078       * use the default trust store format and will not provide a PIN when
079       * attempting to read the trust store.
080       *
081       * @param  trustStoreFile  The path to the trust store file to use.  It must
082       *                         not be {@code null}.
083       */
084      public TrustStoreTrustManager(final File trustStoreFile)
085      {
086        this(trustStoreFile.getAbsolutePath(), null, null, true);
087      }
088    
089    
090    
091      /**
092       * Creates a new instance of this trust store trust manager that will trust
093       * all certificates in the specified file within the validity window. It will
094       * use the default trust store format and will not provide a PIN when
095       * attempting to read the trust store.
096       *
097       * @param  trustStoreFile  The path to the trust store file to use.  It must
098       *                         not be {@code null}.
099       */
100      public TrustStoreTrustManager(final String trustStoreFile)
101      {
102        this(trustStoreFile, null, null, true);
103      }
104    
105    
106    
107      /**
108       * Creates a new instance of this trust store trust manager that will trust
109       * all certificates in the specified file with the specified constraints.
110       *
111       * @param  trustStoreFile        The path to the trust store file to use.  It
112       *                               must not be {@code null}.
113       * @param  trustStorePIN         The PIN to use to access the contents of the
114       *                               trust store.  It may be {@code null} if no
115       *                               PIN is required.
116       * @param  trustStoreFormat      The format to use for the trust store.  It
117       *                               may be {@code null} if the default format
118       *                               should be used.
119       * @param  examineValidityDates  Indicates whether to reject certificates if
120       *                               the current time is outside the validity
121       *                               window for the certificate.
122       */
123      public TrustStoreTrustManager(final File trustStoreFile,
124                                    final char[] trustStorePIN,
125                                    final String trustStoreFormat,
126                                    final boolean examineValidityDates)
127      {
128        this(trustStoreFile.getAbsolutePath(), trustStorePIN, trustStoreFormat,
129             examineValidityDates);
130      }
131    
132    
133    
134      /**
135       * Creates a new instance of this trust store trust manager that will trust
136       * all certificates in the specified file with the specified constraints.
137       *
138       * @param  trustStoreFile        The path to the trust store file to use.  It
139       *                               must not be {@code null}.
140       * @param  trustStorePIN         The PIN to use to access the contents of the
141       *                               trust store.  It may be {@code null} if no
142       *                               PIN is required.
143       * @param  trustStoreFormat      The format to use for the trust store.  It
144       *                               may be {@code null} if the default format
145       *                               should be used.
146       * @param  examineValidityDates  Indicates whether to reject certificates if
147       *                               the current time is outside the validity
148       *                               window for the certificate.
149       */
150      public TrustStoreTrustManager(final String trustStoreFile,
151                                    final char[] trustStorePIN,
152                                    final String trustStoreFormat,
153                                    final boolean examineValidityDates)
154      {
155        ensureNotNull(trustStoreFile);
156    
157        this.trustStoreFile       = trustStoreFile;
158        this.trustStorePIN        = trustStorePIN;
159        this.examineValidityDates = examineValidityDates;
160    
161        if (trustStoreFormat == null)
162        {
163          this.trustStoreFormat = KeyStore.getDefaultType();
164        }
165        else
166        {
167          this.trustStoreFormat = trustStoreFormat;
168        }
169      }
170    
171    
172    
173      /**
174       * Retrieves the path to the trust store file to use.
175       *
176       * @return  The path to the trust store file to use.
177       */
178      public String getTrustStoreFile()
179      {
180        return trustStoreFile;
181      }
182    
183    
184    
185      /**
186       * Retrieves the name of the trust store file format.
187       *
188       * @return  The name of the trust store file format.
189       */
190      public String getTrustStoreFormat()
191      {
192        return trustStoreFormat;
193      }
194    
195    
196    
197      /**
198       * Indicate whether to reject certificates if the current time is outside the
199       * validity window for the certificate.
200       *
201       * @return  {@code true} if the certificate validity time should be examined
202       *          and certificates should be rejected if they are expired or not
203       *          yet valid, or {@code false} if certificates should be accepted
204       *          even outside of the validity window.
205       */
206      public boolean examineValidityDates()
207      {
208        return examineValidityDates;
209      }
210    
211    
212    
213      /**
214       * Retrieves a set of trust managers that may be used to determine whether the
215       * provided certificate chain should be trusted.  It will also check the
216       * validity of the provided certificates.
217       *
218       * @param  chain  The certificate chain for which to make the determination.
219       *
220       * @return  The set of trust managers that may be used to make the
221       *          determination.
222       *
223       * @throws  CertificateException  If the provided client certificate chain
224       *                                should not be trusted.
225       */
226      private synchronized X509TrustManager[] getTrustManagers(
227                                                   final X509Certificate[] chain)
228              throws CertificateException
229      {
230        if (examineValidityDates)
231        {
232          final Date d = new Date();
233          for (final X509Certificate c : chain)
234          {
235            c.checkValidity(d);
236          }
237        }
238    
239        final File f = new File(trustStoreFile);
240        if (! f.exists())
241        {
242          throw new CertificateException(
243               ERR_TRUSTSTORE_NO_SUCH_FILE.get(trustStoreFile));
244        }
245    
246        final KeyStore ks;
247        try
248        {
249          ks = KeyStore.getInstance(trustStoreFormat);
250        }
251        catch (Exception e)
252        {
253          debugException(e);
254    
255          throw new CertificateException(
256               ERR_TRUSTSTORE_UNSUPPORTED_FORMAT.get(trustStoreFormat), e);
257        }
258    
259        FileInputStream inputStream = null;
260        try
261        {
262          inputStream = new FileInputStream(f);
263          ks.load(inputStream, trustStorePIN);
264        }
265        catch (Exception e)
266        {
267          debugException(e);
268    
269          throw new CertificateException(
270               ERR_TRUSTSTORE_CANNOT_LOAD.get(trustStoreFile, trustStoreFormat,
271                                              String.valueOf(e)),
272               e);
273        }
274        finally
275        {
276          if (inputStream != null)
277          {
278            try
279            {
280              inputStream.close();
281            }
282            catch (Exception e)
283            {
284              debugException(e);
285            }
286          }
287        }
288    
289        try
290        {
291          final TrustManagerFactory factory = TrustManagerFactory.getInstance(
292               TrustManagerFactory.getDefaultAlgorithm());
293          factory.init(ks);
294          final TrustManager[] trustManagers = factory.getTrustManagers();
295          final X509TrustManager[] x509TrustManagers =
296               new X509TrustManager[trustManagers.length];
297          for (int i=0; i < trustManagers.length; i++)
298          {
299            x509TrustManagers[i] = (X509TrustManager) trustManagers[i];
300          }
301          return x509TrustManagers;
302        }
303        catch (Exception e)
304        {
305          debugException(e);
306    
307          throw new CertificateException(
308               ERR_TRUSTSTORE_CANNOT_GET_TRUST_MANAGERS.get(trustStoreFile,
309                    trustStoreFormat, String.valueOf(e)),
310               e);
311        }
312      }
313    
314    
315    
316      /**
317       * Checks to determine whether the provided client certificate chain should be
318       * trusted.
319       *
320       * @param  chain     The client certificate chain for which to make the
321       *                   determination.
322       * @param  authType  The authentication type based on the client certificate.
323       *
324       * @throws  CertificateException  If the provided client certificate chain
325       *                                should not be trusted.
326       */
327      public synchronized void checkClientTrusted(final X509Certificate[] chain,
328                                    final String authType)
329             throws CertificateException
330      {
331        for (final X509TrustManager m : getTrustManagers(chain))
332        {
333          m.checkClientTrusted(chain, authType);
334        }
335      }
336    
337    
338    
339      /**
340       * Checks to determine whether the provided server certificate chain should be
341       * trusted.
342       *
343       * @param  chain     The server certificate chain for which to make the
344       *                   determination.
345       * @param  authType  The key exchange algorithm used.
346       *
347       * @throws  CertificateException  If the provided server certificate chain
348       *                                should not be trusted.
349       */
350      public synchronized void checkServerTrusted(final X509Certificate[] chain,
351                                    final String authType)
352             throws CertificateException
353      {
354        for (final X509TrustManager m : getTrustManagers(chain))
355        {
356          m.checkServerTrusted(chain, authType);
357        }
358      }
359    
360    
361    
362      /**
363       * Retrieves the accepted issuer certificates for this trust manager.  This
364       * will always return an empty array.
365       *
366       * @return  The accepted issuer certificates for this trust manager.
367       */
368      public synchronized X509Certificate[] getAcceptedIssuers()
369      {
370        return new X509Certificate[0];
371      }
372    }