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