001/* 002 * Copyright 2012-2019 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2012-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.security.cert.CertificateException; 026import java.security.cert.X509Certificate; 027import java.util.ArrayList; 028import java.util.Collection; 029import java.util.Collections; 030import java.util.List; 031import javax.net.ssl.X509TrustManager; 032 033import com.unboundid.util.Debug; 034import com.unboundid.util.NotMutable; 035import com.unboundid.util.StaticUtils; 036import com.unboundid.util.ThreadSafety; 037import com.unboundid.util.ThreadSafetyLevel; 038import com.unboundid.util.Validator; 039 040import static com.unboundid.util.ssl.SSLMessages.*; 041 042 043 044/** 045 * This class provides an SSL trust manager that has the ability to delegate the 046 * determination about whether to trust a given certificate to one or more other 047 * trust managers. It can be configured to use a logical AND (i.e., all 048 * associated trust managers must be satisfied) or a logical OR (i.e., at least 049 * one of the associated trust managers must be satisfied). 050 */ 051@NotMutable() 052@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 053public final class AggregateTrustManager 054 implements X509TrustManager 055{ 056 /** 057 * A pre-allocated empty certificate array. 058 */ 059 private static final X509Certificate[] NO_CERTIFICATES = 060 new X509Certificate[0]; 061 062 063 064 // Indicates whether to require all of the associated trust managers to accept 065 // a presented certificate, or just to require at least one of them to accept 066 // the certificate. 067 private final boolean requireAllAccepted; 068 069 // The trust managers that will be used to ultimately make the determination. 070 private final List<X509TrustManager> trustManagers; 071 072 073 074 /** 075 * Creates a new aggregate trust manager with the provided information. 076 * 077 * @param requireAllAccepted Indicates whether all of the associated trust 078 * managers must accept a presented certificate 079 * for it to be allowed, or just at least one of 080 * them. 081 * @param trustManagers The set of trust managers to use to make the 082 * determination. It must not be {@code null} or 083 * empty. 084 */ 085 public AggregateTrustManager(final boolean requireAllAccepted, 086 final X509TrustManager ... trustManagers) 087 { 088 this(requireAllAccepted, StaticUtils.toList(trustManagers)); 089 } 090 091 092 093 /** 094 * Creates a new aggregate trust manager with the provided information. 095 * 096 * @param requireAllAccepted Indicates whether all of the associated trust 097 * managers must accept a presented certificate 098 * for it to be allowed, or just at least one of 099 * them. 100 * @param trustManagers The set of trust managers to use to make the 101 * determination. It must not be {@code null} or 102 * empty. 103 */ 104 public AggregateTrustManager(final boolean requireAllAccepted, 105 final Collection<X509TrustManager > trustManagers) 106 { 107 Validator.ensureNotNull(trustManagers); 108 Validator.ensureFalse(trustManagers.isEmpty(), 109 "The set of associated trust managers must not be empty."); 110 111 this.requireAllAccepted = requireAllAccepted; 112 this.trustManagers = 113 Collections.unmodifiableList(new ArrayList<>(trustManagers)); 114 } 115 116 117 118 /** 119 * Indicates whether all of the associated trust managers will be required to 120 * accept a given certificate for it to be considered acceptable. 121 * 122 * @return {@code true} if all of the associated trust managers will be 123 * required to accept the provided certificate chain, or 124 * {@code false} if it will be acceptable for at least one trust 125 * manager to accept the chain even if one or more others do not. 126 */ 127 public boolean requireAllAccepted() 128 { 129 return requireAllAccepted; 130 } 131 132 133 134 /** 135 * Retrieves the set of trust managers that will be used to perform the 136 * validation. 137 * 138 * @return The set of trust managers that will be used to perform the 139 * validation. 140 */ 141 public List<X509TrustManager> getAssociatedTrustManagers() 142 { 143 return trustManagers; 144 } 145 146 147 148 /** 149 * Checks to determine whether the provided client certificate chain should be 150 * trusted. 151 * 152 * @param chain The client certificate chain for which to make the 153 * determination. 154 * @param authType The authentication type based on the client certificate. 155 * 156 * @throws CertificateException If the provided client certificate chain 157 * should not be trusted. 158 */ 159 @Override() 160 public void checkClientTrusted(final X509Certificate[] chain, 161 final String authType) 162 throws CertificateException 163 { 164 ArrayList<String> exceptionMessages = null; 165 166 for (final X509TrustManager m : trustManagers) 167 { 168 try 169 { 170 m.checkClientTrusted(chain, authType); 171 172 if (! requireAllAccepted) 173 { 174 return; 175 } 176 } 177 catch (final CertificateException ce) 178 { 179 Debug.debugException(ce); 180 181 if (requireAllAccepted) 182 { 183 throw ce; 184 } 185 else 186 { 187 if (exceptionMessages == null) 188 { 189 exceptionMessages = new ArrayList<>(trustManagers.size()); 190 } 191 192 exceptionMessages.add(ce.getMessage()); 193 } 194 } 195 } 196 197 // If we've gotten here and there are one or more exception messages, then 198 // it means that none of the associated trust managers accepted the 199 // certificate. 200 if ((exceptionMessages != null) && (! exceptionMessages.isEmpty())) 201 { 202 if (exceptionMessages.size() == 1) 203 { 204 throw new CertificateException(exceptionMessages.get(0)); 205 } 206 else 207 { 208 throw new CertificateException( 209 ERR_AGGREGATE_TRUST_MANAGER_NONE_TRUSTED.get( 210 SSLUtil.certificateToString(chain[0]), 211 StaticUtils.concatenateStrings(exceptionMessages))); 212 } 213 } 214 } 215 216 217 218 /** 219 * Checks to determine whether the provided server certificate chain should be 220 * trusted. 221 * 222 * @param chain The server certificate chain for which to make the 223 * determination. 224 * @param authType The key exchange algorithm used. 225 * 226 * @throws CertificateException If the provided server certificate chain 227 * should not be trusted. 228 */ 229 @Override() 230 public void checkServerTrusted(final X509Certificate[] chain, 231 final String authType) 232 throws CertificateException 233 { 234 ArrayList<String> exceptionMessages = null; 235 236 for (final X509TrustManager m : trustManagers) 237 { 238 try 239 { 240 m.checkServerTrusted(chain, authType); 241 242 if (! requireAllAccepted) 243 { 244 return; 245 } 246 } 247 catch (final CertificateException ce) 248 { 249 Debug.debugException(ce); 250 251 if (requireAllAccepted) 252 { 253 throw ce; 254 } 255 else 256 { 257 if (exceptionMessages == null) 258 { 259 exceptionMessages = new ArrayList<>(trustManagers.size()); 260 } 261 262 exceptionMessages.add(ce.getMessage()); 263 } 264 } 265 } 266 267 // If we've gotten here and there are one or more exception messages, then 268 // it means that none of the associated trust managers accepted the 269 // certificate. 270 if ((exceptionMessages != null) && (! exceptionMessages.isEmpty())) 271 { 272 if (exceptionMessages.size() == 1) 273 { 274 throw new CertificateException(exceptionMessages.get(0)); 275 } 276 else 277 { 278 throw new CertificateException( 279 ERR_AGGREGATE_TRUST_MANAGER_NONE_TRUSTED.get( 280 SSLUtil.certificateToString(chain[0]), 281 StaticUtils.concatenateStrings(exceptionMessages))); 282 } 283 } 284 } 285 286 287 288 /** 289 * Retrieves the accepted issuer certificates for this trust manager. This 290 * will always return an empty array. 291 * 292 * @return The accepted issuer certificates for this trust manager. 293 */ 294 @Override() 295 public X509Certificate[] getAcceptedIssuers() 296 { 297 return NO_CERTIFICATES; 298 } 299}