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
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 }