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