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