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