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