001/*
002 * Copyright 2008-2017 UnboundID Corp.
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2008-2017 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 */
021package com.unboundid.util.ssl;
022
023
024
025import java.io.File;
026import java.io.FileInputStream;
027import java.io.Serializable;
028import java.security.KeyStore;
029import java.security.cert.CertificateException;
030import java.security.cert.X509Certificate;
031import java.util.Date;
032import javax.net.ssl.TrustManager;
033import javax.net.ssl.TrustManagerFactory;
034import javax.net.ssl.X509TrustManager;
035
036import com.unboundid.util.NotMutable;
037import com.unboundid.util.ThreadSafety;
038import com.unboundid.util.ThreadSafetyLevel;
039
040import static com.unboundid.util.Debug.*;
041import static com.unboundid.util.Validator.*;
042import 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)
055public 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}