001/* 002 * Copyright 2008-2017 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2008-2017 Ping Identity Corporation 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 * A pre-allocated empty certificate array. 060 */ 061 private static final X509Certificate[] NO_CERTIFICATES = 062 new X509Certificate[0]; 063 064 065 066 /** 067 * The serial version UID for this serializable class. 068 */ 069 private static final long serialVersionUID = -4093869102727719415L; 070 071 072 073 // Indicates whether to automatically trust expired or not-yet-valid 074 // certificates. 075 private final boolean examineValidityDates; 076 077 // The PIN to use to access the trust store. 078 private final char[] trustStorePIN; 079 080 // The path to the trust store file. 081 private final String trustStoreFile; 082 083 // The format to use for the trust store file. 084 private final String trustStoreFormat; 085 086 087 088 /** 089 * Creates a new instance of this trust store trust manager that will trust 090 * all certificates in the specified file within the validity window. It will 091 * use the default trust store format and will not provide a PIN when 092 * attempting to read the trust store. 093 * 094 * @param trustStoreFile The path to the trust store file to use. It must 095 * not be {@code null}. 096 */ 097 public TrustStoreTrustManager(final File trustStoreFile) 098 { 099 this(trustStoreFile.getAbsolutePath(), null, null, true); 100 } 101 102 103 104 /** 105 * Creates a new instance of this trust store trust manager that will trust 106 * all certificates in the specified file within the validity window. It will 107 * use the default trust store format and will not provide a PIN when 108 * attempting to read the trust store. 109 * 110 * @param trustStoreFile The path to the trust store file to use. It must 111 * not be {@code null}. 112 */ 113 public TrustStoreTrustManager(final String trustStoreFile) 114 { 115 this(trustStoreFile, null, null, true); 116 } 117 118 119 120 /** 121 * Creates a new instance of this trust store trust manager that will trust 122 * all certificates in the specified file with the specified constraints. 123 * 124 * @param trustStoreFile The path to the trust store file to use. It 125 * must not be {@code null}. 126 * @param trustStorePIN The PIN to use to access the contents of the 127 * trust store. It may be {@code null} if no 128 * PIN is required. 129 * @param trustStoreFormat The format to use for the trust store. It 130 * may be {@code null} if the default format 131 * should be used. 132 * @param examineValidityDates Indicates whether to reject certificates if 133 * the current time is outside the validity 134 * window for the certificate. 135 */ 136 public TrustStoreTrustManager(final File trustStoreFile, 137 final char[] trustStorePIN, 138 final String trustStoreFormat, 139 final boolean examineValidityDates) 140 { 141 this(trustStoreFile.getAbsolutePath(), trustStorePIN, trustStoreFormat, 142 examineValidityDates); 143 } 144 145 146 147 /** 148 * Creates a new instance of this trust store trust manager that will trust 149 * all certificates in the specified file with the specified constraints. 150 * 151 * @param trustStoreFile The path to the trust store file to use. It 152 * must not be {@code null}. 153 * @param trustStorePIN The PIN to use to access the contents of the 154 * trust store. It may be {@code null} if no 155 * PIN is required. 156 * @param trustStoreFormat The format to use for the trust store. It 157 * may be {@code null} if the default format 158 * should be used. 159 * @param examineValidityDates Indicates whether to reject certificates if 160 * the current time is outside the validity 161 * window for the certificate. 162 */ 163 public TrustStoreTrustManager(final String trustStoreFile, 164 final char[] trustStorePIN, 165 final String trustStoreFormat, 166 final boolean examineValidityDates) 167 { 168 ensureNotNull(trustStoreFile); 169 170 this.trustStoreFile = trustStoreFile; 171 this.trustStorePIN = trustStorePIN; 172 this.examineValidityDates = examineValidityDates; 173 174 if (trustStoreFormat == null) 175 { 176 this.trustStoreFormat = KeyStore.getDefaultType(); 177 } 178 else 179 { 180 this.trustStoreFormat = trustStoreFormat; 181 } 182 } 183 184 185 186 /** 187 * Retrieves the path to the trust store file to use. 188 * 189 * @return The path to the trust store file to use. 190 */ 191 public String getTrustStoreFile() 192 { 193 return trustStoreFile; 194 } 195 196 197 198 /** 199 * Retrieves the name of the trust store file format. 200 * 201 * @return The name of the trust store file format. 202 */ 203 public String getTrustStoreFormat() 204 { 205 return trustStoreFormat; 206 } 207 208 209 210 /** 211 * Indicate whether to reject certificates if the current time is outside the 212 * validity window for the certificate. 213 * 214 * @return {@code true} if the certificate validity time should be examined 215 * and certificates should be rejected if they are expired or not 216 * yet valid, or {@code false} if certificates should be accepted 217 * even outside of the validity window. 218 */ 219 public boolean examineValidityDates() 220 { 221 return examineValidityDates; 222 } 223 224 225 226 /** 227 * Retrieves a set of trust managers that may be used to determine whether the 228 * provided certificate chain should be trusted. It will also check the 229 * validity of the provided certificates. 230 * 231 * @param chain The certificate chain for which to make the determination. 232 * 233 * @return The set of trust managers that may be used to make the 234 * determination. 235 * 236 * @throws CertificateException If the provided client certificate chain 237 * should not be trusted. 238 */ 239 private synchronized X509TrustManager[] getTrustManagers( 240 final X509Certificate[] chain) 241 throws CertificateException 242 { 243 if (examineValidityDates) 244 { 245 final Date d = new Date(); 246 for (final X509Certificate c : chain) 247 { 248 c.checkValidity(d); 249 } 250 } 251 252 final File f = new File(trustStoreFile); 253 if (! f.exists()) 254 { 255 throw new CertificateException( 256 ERR_TRUSTSTORE_NO_SUCH_FILE.get(trustStoreFile)); 257 } 258 259 final KeyStore ks; 260 try 261 { 262 ks = KeyStore.getInstance(trustStoreFormat); 263 } 264 catch (final Exception e) 265 { 266 debugException(e); 267 268 throw new CertificateException( 269 ERR_TRUSTSTORE_UNSUPPORTED_FORMAT.get(trustStoreFormat), e); 270 } 271 272 FileInputStream inputStream = null; 273 try 274 { 275 inputStream = new FileInputStream(f); 276 ks.load(inputStream, trustStorePIN); 277 } 278 catch (final Exception e) 279 { 280 debugException(e); 281 282 throw new CertificateException( 283 ERR_TRUSTSTORE_CANNOT_LOAD.get(trustStoreFile, trustStoreFormat, 284 String.valueOf(e)), 285 e); 286 } 287 finally 288 { 289 if (inputStream != null) 290 { 291 try 292 { 293 inputStream.close(); 294 } 295 catch (final Exception e) 296 { 297 debugException(e); 298 } 299 } 300 } 301 302 try 303 { 304 final TrustManagerFactory factory = TrustManagerFactory.getInstance( 305 TrustManagerFactory.getDefaultAlgorithm()); 306 factory.init(ks); 307 final TrustManager[] trustManagers = factory.getTrustManagers(); 308 final X509TrustManager[] x509TrustManagers = 309 new X509TrustManager[trustManagers.length]; 310 for (int i=0; i < trustManagers.length; i++) 311 { 312 x509TrustManagers[i] = (X509TrustManager) trustManagers[i]; 313 } 314 return x509TrustManagers; 315 } 316 catch (final Exception e) 317 { 318 debugException(e); 319 320 throw new CertificateException( 321 ERR_TRUSTSTORE_CANNOT_GET_TRUST_MANAGERS.get(trustStoreFile, 322 trustStoreFormat, String.valueOf(e)), 323 e); 324 } 325 } 326 327 328 329 /** 330 * Checks to determine whether the provided client certificate chain should be 331 * trusted. 332 * 333 * @param chain The client certificate chain for which to make the 334 * determination. 335 * @param authType The authentication type based on the client certificate. 336 * 337 * @throws CertificateException If the provided client certificate chain 338 * should not be trusted. 339 */ 340 @Override() 341 public synchronized void checkClientTrusted(final X509Certificate[] chain, 342 final String authType) 343 throws CertificateException 344 { 345 for (final X509TrustManager m : getTrustManagers(chain)) 346 { 347 m.checkClientTrusted(chain, authType); 348 } 349 } 350 351 352 353 /** 354 * Checks to determine whether the provided server certificate chain should be 355 * trusted. 356 * 357 * @param chain The server certificate chain for which to make the 358 * determination. 359 * @param authType The key exchange algorithm used. 360 * 361 * @throws CertificateException If the provided server certificate chain 362 * should not be trusted. 363 */ 364 @Override() 365 public synchronized void checkServerTrusted(final X509Certificate[] chain, 366 final String authType) 367 throws CertificateException 368 { 369 for (final X509TrustManager m : getTrustManagers(chain)) 370 { 371 m.checkServerTrusted(chain, authType); 372 } 373 } 374 375 376 377 /** 378 * Retrieves the accepted issuer certificates for this trust manager. This 379 * will always return an empty array. 380 * 381 * @return The accepted issuer certificates for this trust manager. 382 */ 383 @Override() 384 public synchronized X509Certificate[] getAcceptedIssuers() 385 { 386 return NO_CERTIFICATES; 387 } 388}