/* * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.security.ssl; import java.net.Socket; import java.io.*; import java.util.*; import java.security.*; import java.security.cert.*; import java.security.cert.Certificate; import javax.net.ssl.*; import sun.security.provider.certpath.AlgorithmChecker; import sun.security.action.GetPropertyAction; public abstract class SSLContextImpl extends SSLContextSpi { private static final Debug debug = Debug.getInstance("ssl"); private final EphemeralKeyManager ephemeralKeyManager; private final SSLSessionContextImpl clientCache; private final SSLSessionContextImpl serverCache; private boolean isInitialized; private X509ExtendedKeyManager keyManager; private X509TrustManager trustManager; private SecureRandom secureRandom; // supported and default protocols private ProtocolList defaultServerProtocolList; private ProtocolList defaultClientProtocolList; private ProtocolList supportedProtocolList; // supported and default cipher suites private CipherSuiteList defaultServerCipherSuiteList; private CipherSuiteList defaultClientCipherSuiteList; private CipherSuiteList supportedCipherSuiteList; SSLContextImpl() { ephemeralKeyManager = new EphemeralKeyManager(); clientCache = new SSLSessionContextImpl(); serverCache = new SSLSessionContextImpl(); } @Override protected void engineInit(KeyManager[] km, TrustManager[] tm, SecureRandom sr) throws KeyManagementException { isInitialized = false; keyManager = chooseKeyManager(km); if (tm == null) { try { TrustManagerFactory tmf = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm()); tmf.init((KeyStore)null); tm = tmf.getTrustManagers(); } catch (Exception e) { // eat } } trustManager = chooseTrustManager(tm); if (sr == null) { secureRandom = JsseJce.getSecureRandom(); } else { if (SunJSSE.isFIPS() && (sr.getProvider() != SunJSSE.cryptoProvider)) { throw new KeyManagementException ("FIPS mode: SecureRandom must be from provider " + SunJSSE.cryptoProvider.getName()); } secureRandom = sr; } /* * The initial delay of seeding the random number generator * could be long enough to cause the initial handshake on our * first connection to timeout and fail. Make sure it is * primed and ready by getting some initial output from it. */ if (debug != null && Debug.isOn("sslctx")) { System.out.println("trigger seeding of SecureRandom"); } secureRandom.nextInt(); if (debug != null && Debug.isOn("sslctx")) { System.out.println("done seeding SecureRandom"); } isInitialized = true; } private X509TrustManager chooseTrustManager(TrustManager[] tm) throws KeyManagementException { // We only use the first instance of X509TrustManager passed to us. for (int i = 0; tm != null && i < tm.length; i++) { if (tm[i] instanceof X509TrustManager) { if (SunJSSE.isFIPS() && !(tm[i] instanceof X509TrustManagerImpl)) { throw new KeyManagementException ("FIPS mode: only SunJSSE TrustManagers may be used"); } if (tm[i] instanceof X509ExtendedTrustManager) { return (X509TrustManager)tm[i]; } else { return new AbstractTrustManagerWrapper( (X509TrustManager)tm[i]); } } } // nothing found, return a dummy X509TrustManager. return DummyX509TrustManager.INSTANCE; } private X509ExtendedKeyManager chooseKeyManager(KeyManager[] kms) throws KeyManagementException { for (int i = 0; kms != null && i < kms.length; i++) { KeyManager km = kms[i]; if (!(km instanceof X509KeyManager)) { continue; } if (SunJSSE.isFIPS()) { // In FIPS mode, require that one of SunJSSE's own keymanagers // is used. Otherwise, we cannot be sure that only keys from // the FIPS token are used. if ((km instanceof X509KeyManagerImpl) || (km instanceof SunX509KeyManagerImpl)) { return (X509ExtendedKeyManager)km; } else { // throw exception, we don't want to silently use the // dummy keymanager without telling the user. throw new KeyManagementException ("FIPS mode: only SunJSSE KeyManagers may be used"); } } if (km instanceof X509ExtendedKeyManager) { return (X509ExtendedKeyManager)km; } if (debug != null && Debug.isOn("sslctx")) { System.out.println( "X509KeyManager passed to " + "SSLContext.init(): need an " + "X509ExtendedKeyManager for SSLEngine use"); } return new AbstractKeyManagerWrapper((X509KeyManager)km); } // nothing found, return a dummy X509ExtendedKeyManager return DummyX509KeyManager.INSTANCE; } @Override protected SSLSocketFactory engineGetSocketFactory() { if (!isInitialized) { throw new IllegalStateException( "SSLContextImpl is not initialized"); } return new SSLSocketFactoryImpl(this); } @Override protected SSLServerSocketFactory engineGetServerSocketFactory() { if (!isInitialized) { throw new IllegalStateException("SSLContext is not initialized"); } return new SSLServerSocketFactoryImpl(this); } @Override protected SSLEngine engineCreateSSLEngine() { if (!isInitialized) { throw new IllegalStateException( "SSLContextImpl is not initialized"); } return new SSLEngineImpl(this); } @Override protected SSLEngine engineCreateSSLEngine(String host, int port) { if (!isInitialized) { throw new IllegalStateException( "SSLContextImpl is not initialized"); } return new SSLEngineImpl(this, host, port); } @Override protected SSLSessionContext engineGetClientSessionContext() { return clientCache; } @Override protected SSLSessionContext engineGetServerSessionContext() { return serverCache; } SecureRandom getSecureRandom() { return secureRandom; } X509ExtendedKeyManager getX509KeyManager() { return keyManager; } X509TrustManager getX509TrustManager() { return trustManager; } EphemeralKeyManager getEphemeralKeyManager() { return ephemeralKeyManager; } abstract SSLParameters getDefaultServerSSLParams(); abstract SSLParameters getDefaultClientSSLParams(); abstract SSLParameters getSupportedSSLParams(); // Get supported ProtocolList. ProtocolList getSuportedProtocolList() { if (supportedProtocolList == null) { supportedProtocolList = new ProtocolList(getSupportedSSLParams().getProtocols()); } return supportedProtocolList; } // Get default ProtocolList. ProtocolList getDefaultProtocolList(boolean roleIsServer) { if (roleIsServer) { if (defaultServerProtocolList == null) { defaultServerProtocolList = new ProtocolList( getDefaultServerSSLParams().getProtocols()); } return defaultServerProtocolList; } else { if (defaultClientProtocolList == null) { defaultClientProtocolList = new ProtocolList( getDefaultClientSSLParams().getProtocols()); } return defaultClientProtocolList; } } // Get supported CipherSuiteList. CipherSuiteList getSupportedCipherSuiteList() { // The maintenance of cipher suites needs to be synchronized. synchronized (this) { // Clear cache of available ciphersuites. clearAvailableCache(); if (supportedCipherSuiteList == null) { supportedCipherSuiteList = getApplicableCipherSuiteList( getSuportedProtocolList(), false); } return supportedCipherSuiteList; } } // Get default CipherSuiteList. CipherSuiteList getDefaultCipherSuiteList(boolean roleIsServer) { // The maintenance of cipher suites needs to be synchronized. synchronized (this) { // Clear cache of available ciphersuites. clearAvailableCache(); if (roleIsServer) { if (defaultServerCipherSuiteList == null) { defaultServerCipherSuiteList = getApplicableCipherSuiteList( getDefaultProtocolList(true), true); } return defaultServerCipherSuiteList; } else { if (defaultClientCipherSuiteList == null) { defaultClientCipherSuiteList = getApplicableCipherSuiteList( getDefaultProtocolList(false), true); } return defaultClientCipherSuiteList; } } } /** * Return whether a protocol list is the original default enabled * protocols. See: SSLSocket/SSLEngine.setEnabledProtocols() */ boolean isDefaultProtocolList(ProtocolList protocols) { return (protocols == defaultServerProtocolList) || (protocols == defaultClientProtocolList); } /* * Return the list of all available CipherSuites with a priority of * minPriority or above. */ private CipherSuiteList getApplicableCipherSuiteList( ProtocolList protocols, boolean onlyEnabled) { int minPriority = CipherSuite.SUPPORTED_SUITES_PRIORITY; if (onlyEnabled) { minPriority = CipherSuite.DEFAULT_SUITES_PRIORITY; } Collection allowedCipherSuites = CipherSuite.allowedCipherSuites(); TreeSet suites = new TreeSet<>(); if (!(protocols.collection().isEmpty()) && protocols.min.v != ProtocolVersion.NONE.v) { for (CipherSuite suite : allowedCipherSuites) { if (!suite.allowed || suite.priority < minPriority) { continue; } if (suite.isAvailable() && suite.obsoleted > protocols.min.v && suite.supported <= protocols.max.v) { if (SSLAlgorithmConstraints.DEFAULT.permits( EnumSet.of(CryptoPrimitive.KEY_AGREEMENT), suite.name, null)) { suites.add(suite); } } else if (debug != null && Debug.isOn("sslctx") && Debug.isOn("verbose")) { if (suite.obsoleted <= protocols.min.v) { System.out.println( "Ignoring obsoleted cipher suite: " + suite); } else if (suite.supported > protocols.max.v) { System.out.println( "Ignoring unsupported cipher suite: " + suite); } else { System.out.println( "Ignoring unavailable cipher suite: " + suite); } } } } return new CipherSuiteList(suites); } /** * Clear cache of available ciphersuites. If we support all ciphers * internally, there is no need to clear the cache and calling this * method has no effect. * * Note that every call to clearAvailableCache() and the maintenance of * cipher suites need to be synchronized with this instance. */ private void clearAvailableCache() { if (CipherSuite.DYNAMIC_AVAILABILITY) { supportedCipherSuiteList = null; defaultServerCipherSuiteList = null; defaultClientCipherSuiteList = null; CipherSuite.BulkCipher.clearAvailableCache(); JsseJce.clearEcAvailable(); } } /* * The SSLContext implementation for TLS/SSL algorithm * * SSL/TLS protocols specify the forward compatibility and version * roll-back attack protections, however, a number of SSL/TLS server * vendors did not implement these aspects properly, and some current * SSL/TLS servers may refuse to talk to a TLS 1.1 or later client. * * Considering above interoperability issues, SunJSSE will not set * TLS 1.1 and TLS 1.2 as the enabled protocols for client by default. * * For SSL/TLS servers, there is no such interoperability issues as * SSL/TLS clients. In SunJSSE, TLS 1.1 or later version will be the * enabled protocols for server by default. * * We may change the behavior when popular TLS/SSL vendors support TLS * forward compatibility properly. * * SSLv2Hello is no longer necessary. This interoperability option was * put in place in the late 90's when SSLv3/TLS1.0 were relatively new * and there were a fair number of SSLv2-only servers deployed. Because * of the security issues in SSLv2, it is rarely (if ever) used, as * deployments should now be using SSLv3 and TLSv1. * * Considering the issues of SSLv2Hello, we should not enable SSLv2Hello * by default. Applications still can use it by enabling SSLv2Hello with * the series of setEnabledProtocols APIs. */ /* * The base abstract SSLContext implementation. * * This abstract class encapsulates supported and the default server * SSL parameters. * * @see SSLContext */ private abstract static class AbstractSSLContext extends SSLContextImpl { // parameters private static final SSLParameters defaultServerSSLParams; private static final SSLParameters supportedSSLParams; static { // supported SSL parameters supportedSSLParams = new SSLParameters(); // candidates for available protocols ProtocolVersion[] candidates; if (SunJSSE.isFIPS()) { supportedSSLParams.setProtocols(new String[] { ProtocolVersion.TLS10.name, ProtocolVersion.TLS11.name, ProtocolVersion.TLS12.name }); candidates = new ProtocolVersion[] { ProtocolVersion.TLS10, ProtocolVersion.TLS11, ProtocolVersion.TLS12 }; } else { supportedSSLParams.setProtocols(new String[] { ProtocolVersion.SSL20Hello.name, ProtocolVersion.SSL30.name, ProtocolVersion.TLS10.name, ProtocolVersion.TLS11.name, ProtocolVersion.TLS12.name }); candidates = new ProtocolVersion[] { ProtocolVersion.SSL20Hello, ProtocolVersion.SSL30, ProtocolVersion.TLS10, ProtocolVersion.TLS11, ProtocolVersion.TLS12 }; } defaultServerSSLParams = new SSLParameters(); defaultServerSSLParams.setProtocols( getAvailableProtocols(candidates).toArray(new String[0])); } @Override SSLParameters getDefaultServerSSLParams() { return defaultServerSSLParams; } @Override SSLParameters getSupportedSSLParams() { return supportedSSLParams; } static List getAvailableProtocols( ProtocolVersion[] protocolCandidates) { List availableProtocols = Collections.emptyList(); if (protocolCandidates != null && protocolCandidates.length != 0) { availableProtocols = new ArrayList<>(protocolCandidates.length); for (ProtocolVersion p : protocolCandidates) { if (ProtocolVersion.availableProtocols.contains(p)) { availableProtocols.add(p.name); } } } return availableProtocols; } } /* * The SSLContext implementation for SSLv3 and TLS10 algorithm * * @see SSLContext */ public static final class TLS10Context extends AbstractSSLContext { private static final SSLParameters defaultClientSSLParams; static { // candidates for available protocols ProtocolVersion[] candidates; if (SunJSSE.isFIPS()) { candidates = new ProtocolVersion[] { ProtocolVersion.TLS10 }; } else { candidates = new ProtocolVersion[] { ProtocolVersion.SSL30, ProtocolVersion.TLS10 }; } defaultClientSSLParams = new SSLParameters(); defaultClientSSLParams.setProtocols( getAvailableProtocols(candidates).toArray(new String[0])); } @Override SSLParameters getDefaultClientSSLParams() { return defaultClientSSLParams; } } /* * The SSLContext implementation for TLS11 algorithm * * @see SSLContext */ public static final class TLS11Context extends AbstractSSLContext { private static final SSLParameters defaultClientSSLParams; static { // candidates for available protocols ProtocolVersion[] candidates; if (SunJSSE.isFIPS()) { candidates = new ProtocolVersion[] { ProtocolVersion.TLS10, ProtocolVersion.TLS11 }; } else { candidates = new ProtocolVersion[] { ProtocolVersion.SSL30, ProtocolVersion.TLS10, ProtocolVersion.TLS11 }; } defaultClientSSLParams = new SSLParameters(); defaultClientSSLParams.setProtocols( getAvailableProtocols(candidates).toArray(new String[0])); } @Override SSLParameters getDefaultClientSSLParams() { return defaultClientSSLParams; } } /* * The SSLContext implementation for TLS12 algorithm * * @see SSLContext */ public static final class TLS12Context extends AbstractSSLContext { private static final SSLParameters defaultClientSSLParams; static { // candidates for available protocols ProtocolVersion[] candidates; if (SunJSSE.isFIPS()) { candidates = new ProtocolVersion[] { ProtocolVersion.TLS10, ProtocolVersion.TLS11, ProtocolVersion.TLS12 }; } else { candidates = new ProtocolVersion[] { ProtocolVersion.SSL30, ProtocolVersion.TLS10, ProtocolVersion.TLS11, ProtocolVersion.TLS12 }; } defaultClientSSLParams = new SSLParameters(); defaultClientSSLParams.setProtocols( getAvailableProtocols(candidates).toArray(new String[0])); } @Override SSLParameters getDefaultClientSSLParams() { return defaultClientSSLParams; } } /* * The SSLContext implementation for customized TLS protocols * * @see SSLContext */ private static class CustomizedSSLContext extends AbstractSSLContext { private static final String PROPERTY_NAME = "jdk.tls.client.protocols"; private static final SSLParameters defaultClientSSLParams; private static IllegalArgumentException reservedException = null; // Don't want a java.lang.LinkageError for illegal system property. // // Please don't throw exception in this static block. Otherwise, // java.lang.LinkageError may be thrown during the instantiation of // the provider service. Instead, let's handle the initialization // exception in constructor. static { // candidates for available protocols ProtocolVersion[] candidates; String property = AccessController.doPrivileged( new GetPropertyAction(PROPERTY_NAME)); if (property == null || property.length() == 0) { // the default enabled client TLS protocols if (SunJSSE.isFIPS()) { candidates = new ProtocolVersion[] { ProtocolVersion.TLS10, ProtocolVersion.TLS11, ProtocolVersion.TLS12 }; } else { candidates = new ProtocolVersion[] { ProtocolVersion.SSL30, ProtocolVersion.TLS10, ProtocolVersion.TLS11, ProtocolVersion.TLS12 }; } } else { // remove double quote marks from beginning/end of the property if (property.length() > 1 && property.charAt(0) == '"' && property.charAt(property.length() - 1) == '"') { property = property.substring(1, property.length() - 1); } String[] protocols = null; if (property != null && property.length() != 0) { protocols = property.split(","); } else { reservedException = new IllegalArgumentException( "No protocol specified in " + PROPERTY_NAME + " system property"); protocols = new String[0]; } candidates = new ProtocolVersion[protocols.length]; for (int i = 0; i < protocols.length; i++) { protocols[i] = protocols[i].trim(); // Is it a supported protocol name? try { candidates[i] = ProtocolVersion.valueOf(protocols[i]); } catch (IllegalArgumentException iae) { reservedException = new IllegalArgumentException( PROPERTY_NAME + ": " + protocols[i] + " is not a standard SSL/TLS protocol name", iae); break; } } if ((reservedException == null) && SunJSSE.isFIPS()) { for (ProtocolVersion protocolVersion : candidates) { if (ProtocolVersion.SSL20Hello.v == protocolVersion.v || ProtocolVersion.SSL30.v == protocolVersion.v) { reservedException = new IllegalArgumentException( PROPERTY_NAME + ": " + protocolVersion + " is not FIPS compliant"); } } } } defaultClientSSLParams = new SSLParameters(); if (reservedException == null) { defaultClientSSLParams.setProtocols( getAvailableProtocols(candidates).toArray(new String[0])); } } protected CustomizedSSLContext() { if (reservedException != null) { throw reservedException; } } @Override SSLParameters getDefaultClientSSLParams() { return defaultClientSSLParams; } } /* * The SSLContext implementation for default "TLS" algorithm * * @see SSLContext */ public static final class TLSContext extends CustomizedSSLContext { // use the default constructor and methods } /* * The SSLContext implementation for default "Default" algorithm * * @see SSLContext */ public static final class DefaultSSLContext extends CustomizedSSLContext { private static final String NONE = "NONE"; private static final String P11KEYSTORE = "PKCS11"; private static volatile SSLContextImpl defaultImpl; private static TrustManager[] defaultTrustManagers; private static KeyManager[] defaultKeyManagers; public DefaultSSLContext() throws Exception { try { super.engineInit(getDefaultKeyManager(), getDefaultTrustManager(), null); } catch (Exception e) { if (debug != null && Debug.isOn("defaultctx")) { System.out.println("default context init failed: " + e); } throw e; } if (defaultImpl == null) { defaultImpl = this; } } @Override protected void engineInit(KeyManager[] km, TrustManager[] tm, SecureRandom sr) throws KeyManagementException { throw new KeyManagementException ("Default SSLContext is initialized automatically"); } static synchronized SSLContextImpl getDefaultImpl() throws Exception { if (defaultImpl == null) { new DefaultSSLContext(); } return defaultImpl; } private static synchronized TrustManager[] getDefaultTrustManager() throws Exception { if (defaultTrustManagers != null) { return defaultTrustManagers; } KeyStore ks = TrustManagerFactoryImpl.getCacertsKeyStore("defaultctx"); TrustManagerFactory tmf = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm()); tmf.init(ks); defaultTrustManagers = tmf.getTrustManagers(); return defaultTrustManagers; } private static synchronized KeyManager[] getDefaultKeyManager() throws Exception { if (defaultKeyManagers != null) { return defaultKeyManagers; } final Map props = new HashMap<>(); AccessController.doPrivileged( new PrivilegedExceptionAction() { @Override public Object run() throws Exception { props.put("keyStore", System.getProperty( "javax.net.ssl.keyStore", "")); props.put("keyStoreType", System.getProperty( "javax.net.ssl.keyStoreType", KeyStore.getDefaultType())); props.put("keyStoreProvider", System.getProperty( "javax.net.ssl.keyStoreProvider", "")); props.put("keyStorePasswd", System.getProperty( "javax.net.ssl.keyStorePassword", "")); return null; } }); final String defaultKeyStore = props.get("keyStore"); String defaultKeyStoreType = props.get("keyStoreType"); String defaultKeyStoreProvider = props.get("keyStoreProvider"); if (debug != null && Debug.isOn("defaultctx")) { System.out.println("keyStore is : " + defaultKeyStore); System.out.println("keyStore type is : " + defaultKeyStoreType); System.out.println("keyStore provider is : " + defaultKeyStoreProvider); } if (P11KEYSTORE.equals(defaultKeyStoreType) && !NONE.equals(defaultKeyStore)) { throw new IllegalArgumentException("if keyStoreType is " + P11KEYSTORE + ", then keyStore must be " + NONE); } FileInputStream fs = null; KeyStore ks = null; char[] passwd = null; try { if (defaultKeyStore.length() != 0 && !NONE.equals(defaultKeyStore)) { fs = AccessController.doPrivileged( new PrivilegedExceptionAction() { @Override public FileInputStream run() throws Exception { return new FileInputStream(defaultKeyStore); } }); } String defaultKeyStorePassword = props.get("keyStorePasswd"); if (defaultKeyStorePassword.length() != 0) { passwd = defaultKeyStorePassword.toCharArray(); } /** * Try to initialize key store. */ if ((defaultKeyStoreType.length()) != 0) { if (debug != null && Debug.isOn("defaultctx")) { System.out.println("init keystore"); } if (defaultKeyStoreProvider.length() == 0) { ks = KeyStore.getInstance(defaultKeyStoreType); } else { ks = KeyStore.getInstance(defaultKeyStoreType, defaultKeyStoreProvider); } // if defaultKeyStore is NONE, fs will be null ks.load(fs, passwd); } } finally { if (fs != null) { fs.close(); fs = null; } } /* * Try to initialize key manager. */ if (debug != null && Debug.isOn("defaultctx")) { System.out.println("init keymanager of type " + KeyManagerFactory.getDefaultAlgorithm()); } KeyManagerFactory kmf = KeyManagerFactory.getInstance( KeyManagerFactory.getDefaultAlgorithm()); if (P11KEYSTORE.equals(defaultKeyStoreType)) { kmf.init(ks, null); // do not pass key passwd if using token } else { kmf.init(ks, passwd); } defaultKeyManagers = kmf.getKeyManagers(); return defaultKeyManagers; } } } final class AbstractTrustManagerWrapper extends X509ExtendedTrustManager implements X509TrustManager { // the delegated trust manager private final X509TrustManager tm; AbstractTrustManagerWrapper(X509TrustManager tm) { this.tm = tm; } @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { tm.checkClientTrusted(chain, authType); } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { tm.checkServerTrusted(chain, authType); } @Override public X509Certificate[] getAcceptedIssuers() { return tm.getAcceptedIssuers(); } @Override public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { tm.checkClientTrusted(chain, authType); checkAdditionalTrust(chain, authType, socket, true); } @Override public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { tm.checkServerTrusted(chain, authType); checkAdditionalTrust(chain, authType, socket, false); } @Override public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { tm.checkClientTrusted(chain, authType); checkAdditionalTrust(chain, authType, engine, true); } @Override public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { tm.checkServerTrusted(chain, authType); checkAdditionalTrust(chain, authType, engine, false); } private void checkAdditionalTrust(X509Certificate[] chain, String authType, Socket socket, boolean isClient) throws CertificateException { if (socket != null && socket.isConnected() && socket instanceof SSLSocket) { SSLSocket sslSocket = (SSLSocket)socket; SSLSession session = sslSocket.getHandshakeSession(); if (session == null) { throw new CertificateException("No handshake session"); } // check endpoint identity String identityAlg = sslSocket.getSSLParameters(). getEndpointIdentificationAlgorithm(); if (identityAlg != null && identityAlg.length() != 0) { String hostname = session.getPeerHost(); X509TrustManagerImpl.checkIdentity( hostname, chain[0], identityAlg); } // try the best to check the algorithm constraints ProtocolVersion protocolVersion = ProtocolVersion.valueOf(session.getProtocol()); AlgorithmConstraints constraints = null; if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (session instanceof ExtendedSSLSession) { ExtendedSSLSession extSession = (ExtendedSSLSession)session; String[] peerSupportedSignAlgs = extSession.getLocalSupportedSignatureAlgorithms(); constraints = new SSLAlgorithmConstraints( sslSocket, peerSupportedSignAlgs, true); } else { constraints = new SSLAlgorithmConstraints(sslSocket, true); } } else { constraints = new SSLAlgorithmConstraints(sslSocket, true); } checkAlgorithmConstraints(chain, constraints); } } private void checkAdditionalTrust(X509Certificate[] chain, String authType, SSLEngine engine, boolean isClient) throws CertificateException { if (engine != null) { SSLSession session = engine.getHandshakeSession(); if (session == null) { throw new CertificateException("No handshake session"); } // check endpoint identity String identityAlg = engine.getSSLParameters(). getEndpointIdentificationAlgorithm(); if (identityAlg != null && identityAlg.length() != 0) { String hostname = session.getPeerHost(); X509TrustManagerImpl.checkIdentity( hostname, chain[0], identityAlg); } // try the best to check the algorithm constraints ProtocolVersion protocolVersion = ProtocolVersion.valueOf(session.getProtocol()); AlgorithmConstraints constraints = null; if (protocolVersion.v >= ProtocolVersion.TLS12.v) { if (session instanceof ExtendedSSLSession) { ExtendedSSLSession extSession = (ExtendedSSLSession)session; String[] peerSupportedSignAlgs = extSession.getLocalSupportedSignatureAlgorithms(); constraints = new SSLAlgorithmConstraints( engine, peerSupportedSignAlgs, true); } else { constraints = new SSLAlgorithmConstraints(engine, true); } } else { constraints = new SSLAlgorithmConstraints(engine, true); } checkAlgorithmConstraints(chain, constraints); } } private void checkAlgorithmConstraints(X509Certificate[] chain, AlgorithmConstraints constraints) throws CertificateException { try { // Does the certificate chain end with a trusted certificate? int checkedLength = chain.length - 1; Collection trustedCerts = new HashSet<>(); X509Certificate[] certs = tm.getAcceptedIssuers(); if ((certs != null) && (certs.length > 0)){ Collections.addAll(trustedCerts, certs); } if (trustedCerts.contains(chain[checkedLength])) { checkedLength--; } // A forward checker, need to check from trust to target if (checkedLength >= 0) { AlgorithmChecker checker = new AlgorithmChecker(constraints); checker.init(false); for (int i = checkedLength; i >= 0; i--) { Certificate cert = chain[i]; // We don't care about the unresolved critical extensions. checker.check(cert, Collections.emptySet()); } } } catch (CertPathValidatorException cpve) { throw new CertificateException( "Certificates does not conform to algorithm constraints"); } } } // Dummy X509TrustManager implementation, rejects all peer certificates. // Used if the application did not specify a proper X509TrustManager. final class DummyX509TrustManager extends X509ExtendedTrustManager implements X509TrustManager { static final X509TrustManager INSTANCE = new DummyX509TrustManager(); private DummyX509TrustManager() { // empty } /* * Given the partial or complete certificate chain * provided by the peer, build a certificate path * to a trusted root and return if it can be * validated and is trusted for client SSL authentication. * If not, it throws an exception. */ @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { throw new CertificateException( "No X509TrustManager implementation avaiable"); } /* * Given the partial or complete certificate chain * provided by the peer, build a certificate path * to a trusted root and return if it can be * validated and is trusted for server SSL authentication. * If not, it throws an exception. */ @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { throw new CertificateException( "No X509TrustManager implementation available"); } /* * Return an array of issuer certificates which are trusted * for authenticating peers. */ @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } @Override public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { throw new CertificateException( "No X509TrustManager implementation available"); } @Override public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) throws CertificateException { throw new CertificateException( "No X509TrustManager implementation available"); } @Override public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { throw new CertificateException( "No X509TrustManager implementation available"); } @Override public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) throws CertificateException { throw new CertificateException( "No X509TrustManager implementation available"); } } /* * A wrapper class to turn a X509KeyManager into an X509ExtendedKeyManager */ final class AbstractKeyManagerWrapper extends X509ExtendedKeyManager { private final X509KeyManager km; AbstractKeyManagerWrapper(X509KeyManager km) { this.km = km; } @Override public String[] getClientAliases(String keyType, Principal[] issuers) { return km.getClientAliases(keyType, issuers); } @Override public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) { return km.chooseClientAlias(keyType, issuers, socket); } @Override public String[] getServerAliases(String keyType, Principal[] issuers) { return km.getServerAliases(keyType, issuers); } @Override public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) { return km.chooseServerAlias(keyType, issuers, socket); } @Override public X509Certificate[] getCertificateChain(String alias) { return km.getCertificateChain(alias); } @Override public PrivateKey getPrivateKey(String alias) { return km.getPrivateKey(alias); } // Inherit chooseEngineClientAlias() and chooseEngineServerAlias() from // X509ExtendedKeymanager. It defines them to return null; } // Dummy X509KeyManager implementation, never returns any certificates/keys. // Used if the application did not specify a proper X509TrustManager. final class DummyX509KeyManager extends X509ExtendedKeyManager { static final X509ExtendedKeyManager INSTANCE = new DummyX509KeyManager(); private DummyX509KeyManager() { // empty } /* * Get the matching aliases for authenticating the client side of a secure * socket given the public key type and the list of * certificate issuer authorities recognized by the peer (if any). */ @Override public String[] getClientAliases(String keyType, Principal[] issuers) { return null; } /* * Choose an alias to authenticate the client side of a secure * socket given the public key type and the list of * certificate issuer authorities recognized by the peer (if any). */ @Override public String chooseClientAlias(String[] keyTypes, Principal[] issuers, Socket socket) { return null; } /* * Choose an alias to authenticate the client side of an * engine given the public key type and the list of * certificate issuer authorities recognized by the peer (if any). */ @Override public String chooseEngineClientAlias( String[] keyTypes, Principal[] issuers, SSLEngine engine) { return null; } /* * Get the matching aliases for authenticating the server side of a secure * socket given the public key type and the list of * certificate issuer authorities recognized by the peer (if any). */ @Override public String[] getServerAliases(String keyType, Principal[] issuers) { return null; } /* * Choose an alias to authenticate the server side of a secure * socket given the public key type and the list of * certificate issuer authorities recognized by the peer (if any). */ @Override public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) { return null; } /* * Choose an alias to authenticate the server side of an engine * given the public key type and the list of * certificate issuer authorities recognized by the peer (if any). */ @Override public String chooseEngineServerAlias( String keyType, Principal[] issuers, SSLEngine engine) { return null; } /** * Returns the certificate chain associated with the given alias. * * @param alias the alias name * * @return the certificate chain (ordered with the user's certificate first * and the root certificate authority last) */ @Override public X509Certificate[] getCertificateChain(String alias) { return null; } /* * Returns the key associated with the given alias, using the given * password to recover it. * * @param alias the alias name * * @return the requested key */ @Override public PrivateKey getPrivateKey(String alias) { return null; } }