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