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