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