1 /* 2 * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.security.ssl; 27 28 import java.net.Socket; 29 import java.security.*; 30 import java.security.cert.*; 31 import java.util.*; 32 import javax.net.ssl.*; 33 import sun.security.util.AnchorCertificates; 34 import sun.security.util.HostnameChecker; 35 import sun.security.validator.*; 36 37 /** 38 * This class implements the SunJSSE X.509 trust manager using the internal 39 * validator API in J2SE core. The logic in this class is minimal.<p> 40 * <p> 41 * This class supports both the Simple validation algorithm from previous 42 * JSSE versions and PKIX validation. Currently, it is not possible for the 43 * application to specify PKIX parameters other than trust anchors. This will 44 * be fixed in a future release using new APIs. When that happens, it may also 45 * make sense to separate the Simple and PKIX trust managers into separate 46 * classes. 47 * 48 * @author Andreas Sterbenz 49 */ 50 final class X509TrustManagerImpl extends X509ExtendedTrustManager 51 implements X509TrustManager { 52 53 private final String validatorType; 54 55 /** 56 * The Set of trusted X509Certificates. 57 */ 58 private final Collection<X509Certificate> trustedCerts; 59 60 private final PKIXBuilderParameters pkixParams; 61 62 // note that we need separate validator for client and server due to 63 // the different extension checks. They are initialized lazily on demand. 64 private volatile Validator clientValidator, serverValidator; 65 66 X509TrustManagerImpl(String validatorType, 67 Collection<X509Certificate> trustedCerts) { 68 69 this.validatorType = validatorType; 70 this.pkixParams = null; 71 72 if (trustedCerts == null) { 73 trustedCerts = Collections.<X509Certificate>emptySet(); 74 } 75 76 this.trustedCerts = trustedCerts; 77 78 if (SSLLogger.isOn && SSLLogger.isOn("ssl,trustmanager")) { 79 SSLLogger.fine("adding as trusted certificates", 80 (Object[])trustedCerts.toArray(new X509Certificate[0])); 81 } 82 } 83 84 X509TrustManagerImpl(String validatorType, PKIXBuilderParameters params) { 85 this.validatorType = validatorType; 86 this.pkixParams = params; 87 // create server validator eagerly so that we can conveniently 88 // get the trusted certificates 89 // clients need it anyway eventually, and servers will not mind 90 // the little extra footprint 91 Validator v = getValidator(Validator.VAR_TLS_SERVER); 92 trustedCerts = v.getTrustedCertificates(); 93 serverValidator = v; 94 95 if (SSLLogger.isOn && SSLLogger.isOn("ssl,trustmanager")) { 96 SSLLogger.fine("adding as trusted certificates", 97 (Object[])trustedCerts.toArray(new X509Certificate[0])); 98 } 99 } 100 101 @Override 102 public void checkClientTrusted(X509Certificate[] chain, String authType) 103 throws CertificateException { 104 checkTrusted(chain, authType, (Socket)null, true); 105 } 106 107 @Override 108 public void checkServerTrusted(X509Certificate[] chain, String authType) 109 throws CertificateException { 110 checkTrusted(chain, authType, (Socket)null, false); 111 } 112 113 @Override 114 public X509Certificate[] getAcceptedIssuers() { 115 X509Certificate[] certsArray = new X509Certificate[trustedCerts.size()]; 116 trustedCerts.toArray(certsArray); 117 return certsArray; 118 } 119 120 @Override 121 public void checkClientTrusted(X509Certificate[] chain, String authType, 122 Socket socket) throws CertificateException { 123 checkTrusted(chain, authType, socket, true); 124 } 125 126 @Override 127 public void checkServerTrusted(X509Certificate[] chain, String authType, 128 Socket socket) throws CertificateException { 129 checkTrusted(chain, authType, socket, false); 130 } 131 132 @Override 133 public void checkClientTrusted(X509Certificate[] chain, String authType, 134 SSLEngine engine) throws CertificateException { 135 checkTrusted(chain, authType, engine, true); 136 } 137 138 @Override 139 public void checkServerTrusted(X509Certificate[] chain, String authType, 140 SSLEngine engine) throws CertificateException { 141 checkTrusted(chain, authType, engine, false); 142 } 143 144 private Validator checkTrustedInit(X509Certificate[] chain, 145 String authType, boolean isClient) { 146 if (chain == null || chain.length == 0) { 147 throw new IllegalArgumentException( 148 "null or zero-length certificate chain"); 149 } 150 151 if (authType == null || authType.length() == 0) { 152 throw new IllegalArgumentException( 153 "null or zero-length authentication type"); 154 } 155 156 Validator v = null; 157 if (isClient) { 158 v = clientValidator; 159 if (v == null) { 160 synchronized (this) { 161 v = clientValidator; 162 if (v == null) { 163 v = getValidator(Validator.VAR_TLS_CLIENT); 164 clientValidator = v; 165 } 166 } 167 } 168 } else { 169 // assume double checked locking with a volatile flag works 170 // (guaranteed under the new Tiger memory model) 171 v = serverValidator; 172 if (v == null) { 173 synchronized (this) { 174 v = serverValidator; 175 if (v == null) { 176 v = getValidator(Validator.VAR_TLS_SERVER); 177 serverValidator = v; 178 } 179 } 180 } 181 } 182 183 return v; 184 } 185 186 private void checkTrusted(X509Certificate[] chain, String authType, 187 Socket socket, boolean isClient) throws CertificateException { 188 Validator v = checkTrustedInit(chain, authType, isClient); 189 190 X509Certificate[] trustedChain = null; 191 if ((socket != null) && socket.isConnected() && 192 (socket instanceof SSLSocket)) { 193 194 SSLSocket sslSocket = (SSLSocket)socket; 195 SSLSession session = sslSocket.getHandshakeSession(); 196 if (session == null) { 197 throw new CertificateException("No handshake session"); 198 } 199 200 // create the algorithm constraints 201 boolean isExtSession = (session instanceof ExtendedSSLSession); 202 AlgorithmConstraints constraints; 203 if (isExtSession && 204 ProtocolVersion.useTLS12PlusSpec(session.getProtocol())) { 205 ExtendedSSLSession extSession = (ExtendedSSLSession)session; 206 String[] localSupportedSignAlgs = 207 extSession.getLocalSupportedSignatureAlgorithms(); 208 209 constraints = new SSLAlgorithmConstraints( 210 sslSocket, localSupportedSignAlgs, false); 211 } else { 212 constraints = new SSLAlgorithmConstraints(sslSocket, false); 213 } 214 215 // Grab any stapled OCSP responses for use in validation 216 List<byte[]> responseList = Collections.emptyList(); 217 if (!isClient && isExtSession) { 218 responseList = 219 ((ExtendedSSLSession)session).getStatusResponses(); 220 } 221 trustedChain = validate(v, chain, responseList, 222 constraints, isClient ? null : authType); 223 224 // check if EE certificate chains to a public root CA (as 225 // pre-installed in cacerts) 226 boolean chainsToPublicCA = AnchorCertificates.contains( 227 trustedChain[trustedChain.length-1]); 228 229 // check endpoint identity 230 String identityAlg = sslSocket.getSSLParameters(). 231 getEndpointIdentificationAlgorithm(); 232 if (identityAlg != null && identityAlg.length() != 0) { 233 checkIdentity(session, trustedChain[0], identityAlg, isClient, 234 getRequestedServerNames(socket), chainsToPublicCA); 235 } 236 } else { 237 trustedChain = validate(v, chain, Collections.emptyList(), 238 null, isClient ? null : authType); 239 } 240 241 if (SSLLogger.isOn && SSLLogger.isOn("ssl,trustmanager")) { 242 SSLLogger.fine("Found trusted certificate", 243 trustedChain[trustedChain.length - 1]); 244 } 245 } 246 247 private void checkTrusted(X509Certificate[] chain, String authType, 248 SSLEngine engine, boolean isClient) throws CertificateException { 249 Validator v = checkTrustedInit(chain, authType, isClient); 250 251 X509Certificate[] trustedChain = null; 252 if (engine != null) { 253 SSLSession session = engine.getHandshakeSession(); 254 if (session == null) { 255 throw new CertificateException("No handshake session"); 256 } 257 258 // create the algorithm constraints 259 boolean isExtSession = (session instanceof ExtendedSSLSession); 260 AlgorithmConstraints constraints; 261 if (isExtSession && 262 ProtocolVersion.useTLS12PlusSpec(session.getProtocol())) { 263 ExtendedSSLSession extSession = (ExtendedSSLSession)session; 264 String[] localSupportedSignAlgs = 265 extSession.getLocalSupportedSignatureAlgorithms(); 266 267 constraints = new SSLAlgorithmConstraints( 268 engine, localSupportedSignAlgs, false); 269 } else { 270 constraints = new SSLAlgorithmConstraints(engine, false); 271 } 272 273 // Grab any stapled OCSP responses for use in validation 274 List<byte[]> responseList = Collections.emptyList(); 275 if (!isClient && isExtSession) { 276 responseList = 277 ((ExtendedSSLSession)session).getStatusResponses(); 278 } 279 trustedChain = validate(v, chain, responseList, 280 constraints, isClient ? null : authType); 281 282 // check if EE certificate chains to a public root CA (as 283 // pre-installed in cacerts) 284 boolean chainsToPublicCA = AnchorCertificates.contains( 285 trustedChain[trustedChain.length-1]); 286 287 // check endpoint identity 288 String identityAlg = engine.getSSLParameters(). 289 getEndpointIdentificationAlgorithm(); 290 if (identityAlg != null && identityAlg.length() != 0) { 291 checkIdentity(session, trustedChain[0], identityAlg, isClient, 292 getRequestedServerNames(engine), chainsToPublicCA); 293 } 294 } else { 295 trustedChain = validate(v, chain, Collections.emptyList(), 296 null, isClient ? null : authType); 297 } 298 299 if (SSLLogger.isOn && SSLLogger.isOn("ssl,trustmanager")) { 300 SSLLogger.fine("Found trusted certificate", 301 trustedChain[trustedChain.length - 1]); 302 } 303 } 304 305 private Validator getValidator(String variant) { 306 Validator v; 307 if (pkixParams == null) { 308 v = Validator.getInstance(validatorType, variant, trustedCerts); 309 } else { 310 v = Validator.getInstance(validatorType, variant, pkixParams); 311 } 312 return v; 313 } 314 315 private static X509Certificate[] validate(Validator v, 316 X509Certificate[] chain, List<byte[]> responseList, 317 AlgorithmConstraints constraints, String authType) 318 throws CertificateException { 319 Object o = JsseJce.beginFipsProvider(); 320 try { 321 return v.validate(chain, null, responseList, constraints, authType); 322 } finally { 323 JsseJce.endFipsProvider(o); 324 } 325 } 326 327 // Get string representation of HostName from a list of server names. 328 // 329 // We are only accepting host_name name type in the list. 330 private static String getHostNameInSNI(List<SNIServerName> sniNames) { 331 332 SNIHostName hostname = null; 333 for (SNIServerName sniName : sniNames) { 334 if (sniName.getType() != StandardConstants.SNI_HOST_NAME) { 335 continue; 336 } 337 338 if (sniName instanceof SNIHostName) { 339 hostname = (SNIHostName)sniName; 340 } else { 341 try { 342 hostname = new SNIHostName(sniName.getEncoded()); 343 } catch (IllegalArgumentException iae) { 344 // unlikely to happen, just in case ... 345 if (SSLLogger.isOn && SSLLogger.isOn("ssl,trustmanager")) { 346 SSLLogger.fine("Illegal server name: " + sniName); 347 } 348 } 349 } 350 351 // no more than server name of the same name type 352 break; 353 } 354 355 if (hostname != null) { 356 return hostname.getAsciiName(); 357 } 358 359 return null; 360 } 361 362 // Also used by X509KeyManagerImpl 363 static List<SNIServerName> getRequestedServerNames(Socket socket) { 364 if (socket != null && socket.isConnected() && 365 socket instanceof SSLSocket) { 366 367 SSLSocket sslSocket = (SSLSocket)socket; 368 SSLSession session = sslSocket.getHandshakeSession(); 369 370 if (session != null && (session instanceof ExtendedSSLSession)) { 371 ExtendedSSLSession extSession = (ExtendedSSLSession)session; 372 return extSession.getRequestedServerNames(); 373 } 374 } 375 376 return Collections.<SNIServerName>emptyList(); 377 } 378 379 // Also used by X509KeyManagerImpl 380 static List<SNIServerName> getRequestedServerNames(SSLEngine engine) { 381 if (engine != null) { 382 SSLSession session = engine.getHandshakeSession(); 383 384 if (session != null && (session instanceof ExtendedSSLSession)) { 385 ExtendedSSLSession extSession = (ExtendedSSLSession)session; 386 return extSession.getRequestedServerNames(); 387 } 388 } 389 390 return Collections.<SNIServerName>emptyList(); 391 } 392 393 /* 394 * Per RFC 6066, if an application negotiates a server name using an 395 * application protocol and then upgrades to TLS, and if a server_name 396 * extension is sent, then the extension SHOULD contain the same name 397 * that was negotiated in the application protocol. If the server_name 398 * is established in the TLS session handshake, the client SHOULD NOT 399 * attempt to request a different server name at the application layer. 400 * 401 * According to the above spec, we only need to check either the identity 402 * in server_name extension or the peer host of the connection. Peer host 403 * is not always a reliable fully qualified domain name. The HostName in 404 * server_name extension is more reliable than peer host. So we prefer 405 * the identity checking aginst the server_name extension if present, and 406 * may failove to peer host checking. 407 */ 408 private static void checkIdentity(SSLSession session, 409 X509Certificate cert, 410 String algorithm, 411 boolean isClient, 412 List<SNIServerName> sniNames, 413 boolean chainsToPublicCA) throws CertificateException { 414 415 boolean identifiable = false; 416 String peerHost = session.getPeerHost(); 417 if (isClient) { 418 String hostname = getHostNameInSNI(sniNames); 419 if (hostname != null) { 420 try { 421 checkIdentity(hostname, cert, algorithm, chainsToPublicCA); 422 identifiable = true; 423 } catch (CertificateException ce) { 424 if (hostname.equalsIgnoreCase(peerHost)) { 425 throw ce; 426 } 427 428 // otherwisw, failover to check peer host 429 } 430 } 431 } 432 433 if (!identifiable) { 434 checkIdentity(peerHost, cert, algorithm, chainsToPublicCA); 435 } 436 } 437 438 /* 439 * Identify the peer by its certificate and hostname. 440 * 441 * Lifted from sun.net.www.protocol.https.HttpsClient. 442 */ 443 static void checkIdentity(String hostname, X509Certificate cert, 444 String algorithm) throws CertificateException { 445 checkIdentity(hostname, cert, algorithm, false); 446 } 447 448 private static void checkIdentity(String hostname, X509Certificate cert, 449 String algorithm, boolean chainsToPublicCA) 450 throws CertificateException { 451 if (algorithm != null && algorithm.length() != 0) { 452 // if IPv6 strip off the "[]" 453 if ((hostname != null) && hostname.startsWith("[") && 454 hostname.endsWith("]")) { 455 hostname = hostname.substring(1, hostname.length() - 1); 456 } 457 458 if (algorithm.equalsIgnoreCase("HTTPS")) { 459 HostnameChecker.getInstance(HostnameChecker.TYPE_TLS).match( 460 hostname, cert, chainsToPublicCA); 461 } else if (algorithm.equalsIgnoreCase("LDAP") || 462 algorithm.equalsIgnoreCase("LDAPS")) { 463 HostnameChecker.getInstance(HostnameChecker.TYPE_LDAP).match( 464 hostname, cert, chainsToPublicCA); 465 } else { 466 throw new CertificateException( 467 "Unknown identification algorithm: " + algorithm); 468 } 469 } 470 } 471 } 472