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