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