ServerNameExtension(List<SNIServerName> serverNames) throws IOException { super(ExtensionType.EXT_SERVER_NAME); listLength = 0; sniMap = new LinkedHashMap<>(); for (SNIServerName serverName : serverNames) { // check for duplicated server name type if (sniMap.put(serverName.getType(), serverName) != null) { // unlikely to happen, but in case ... throw new RuntimeException( "Duplicated server name of type " + serverName.getType()); } listLength += serverName.getEncoded().length + NAME_HEADER_LENGTH; } // This constructor is used for ClientHello only. Empty list is // not allowed in client mode. if (listLength == 0) { throw new RuntimeException("The ServerNameList cannot be empty"); } }
/** * Returns client ssl engine. * * @param context - SSLContext to get SSLEngine from. * @param useSNI - flag used to enable or disable using SNI extension. * Needed for Kerberos. */ public static SSLEngine getClientSSLEngine( SSLContext context, boolean useSNI) { SSLEngine clientEngine = context.createSSLEngine(HOST, 80); clientEngine.setUseClientMode(true); if (useSNI) { SNIHostName serverName = new SNIHostName(SERVER_NAME); List<SNIServerName> serverNames = new ArrayList<>(); serverNames.add(serverName); SSLParameters params = clientEngine.getSSLParameters(); params.setServerNames(serverNames); clientEngine.setSSLParameters(params); } return clientEngine; }
@Test public void test_setSSLParameters_Socket() throws Exception { assumeJava8(); Socket socket = new OpenSSLSocketFactoryImpl().createSocket(); SSLParametersImpl impl = SSLParametersImpl.getDefault(); SSLParameters params = new SSLParameters(); List<SNIServerName> names = new ArrayList<SNIServerName>(); names.add(new SNIHostName("some.host")); params.setServerNames(names); params.setUseCipherSuitesOrder(false); params.setEndpointIdentificationAlgorithm("ABC"); String[] applicationProtocols = new String[] {"foo", "bar"}; if (isJavaVersion(9)) { setApplicationProtocols(params, applicationProtocols); } Platform.setSSLParameters(params, impl, (AbstractConscryptSocket) socket); assertEquals("some.host", ((AbstractConscryptSocket) socket).getHostname()); assertFalse(impl.getUseCipherSuitesOrder()); assertEquals("ABC", impl.getEndpointIdentificationAlgorithm()); if (isJavaVersion(9)) { assertArrayEquals(applicationProtocols, impl.getApplicationProtocols()); } }
@Test public void test_setSSLParameters_Engine() throws Exception { assumeJava8(); SSLParametersImpl impl = SSLParametersImpl.getDefault(); SSLParameters params = new SSLParameters(); ConscryptEngine engine = new ConscryptEngine(impl); List<SNIServerName> names = new ArrayList<SNIServerName>(); names.add(new SNIHostName("some.host")); params.setServerNames(names); params.setUseCipherSuitesOrder(false); params.setEndpointIdentificationAlgorithm("ABC"); String[] applicationProtocols = new String[] {"foo", "bar"}; if (isJavaVersion(9)) { setApplicationProtocols(params, applicationProtocols); } Platform.setSSLParameters(params, impl, engine); assertEquals("some.host", engine.getHostname()); assertFalse(impl.getUseCipherSuitesOrder()); assertEquals("ABC", impl.getEndpointIdentificationAlgorithm()); if (isJavaVersion(9)) { assertArrayEquals(applicationProtocols, impl.getApplicationProtocols()); } }
@TargetApi(24) private static String getSniHostnameFromParams(SSLParameters params) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { Method m_getServerNames = params.getClass().getMethod("getServerNames"); @SuppressWarnings("unchecked") List<SNIServerName> serverNames = (List<SNIServerName>) m_getServerNames.invoke(params); if (serverNames != null) { for (SNIServerName serverName : serverNames) { if (serverName.getType() == StandardConstants.SNI_HOST_NAME) { return ((SNIHostName) serverName).getAsciiName(); } } } return null; }
public String getServerName() { String ret = serverName; if (sslEngine != null && sslEngine.getSession() != null) { SSLSession session = sslEngine.getSession(); if (session instanceof ExtendedSSLSession) { ExtendedSSLSession es = (ExtendedSSLSession) session; for (SNIServerName sni : es.getRequestedServerNames()) { SNIHostName hn = new SNIHostName(sni.getEncoded()); ret = hn.getAsciiName(); break; } } } LOG.log("HS DONE, servername is " + ret); return ret; }
/** * Method retrieves requested server name from ExtendedSSLSession and * uses it to return proper alias for server certificate * * @param session * @return */ private String chooseServerAlias(ExtendedSSLSession session) { // Pick first SNIHostName in the list of SNI names. String hostname = null; for (SNIServerName name : session.getRequestedServerNames()) { if (name.getType() == StandardConstants.SNI_HOST_NAME) { hostname = ((SNIHostName) name).getAsciiName(); break; } } // If we got given a hostname over SNI, check if we have a cert and // key for that hostname. If so, we use it. // Otherwise, we fall back to the default certificate. if (hostname != null && (getCertificateChain(hostname) != null && getPrivateKey(hostname) != null)) { return hostname; } else { return def_cert_alias; } }
/** * Obtains a <code>List</code> containing all {@link SNIServerName}s * of the requested Server Name Indication (SNI) extension. */ @Override public List<SNIServerName> getRequestedServerNames() { if (requestedServerNames != null && !requestedServerNames.isEmpty()) { return Collections.<SNIServerName>unmodifiableList( requestedServerNames); } return Collections.<SNIServerName>emptyList(); }
List<SNIServerName> getServerNames() { if (sniMap != null && !sniMap.isEmpty()) { return Collections.<SNIServerName>unmodifiableList( new ArrayList<>(sniMap.values())); } return Collections.<SNIServerName>emptyList(); }
boolean isMatched(Collection<SNIMatcher> matchers) { if (sniMap != null && !sniMap.isEmpty()) { for (SNIMatcher matcher : matchers) { SNIServerName sniName = sniMap.get(matcher.getType()); if (sniName != null && (!matcher.matches(sniName))) { return false; } } } return true; }
boolean isIdentical(List<SNIServerName> other) { if (other.size() == sniMap.size()) { for(SNIServerName sniInOther : other) { SNIServerName sniName = sniMap.get(sniInOther.getType()); if (sniName == null || !sniInOther.equals(sniName)) { return false; } } return true; } return false; }
@Override void send(HandshakeOutStream s) throws IOException { s.putInt16(type.id); if (listLength == 0) { s.putInt16(listLength); // in ServerHello, empty extension_data } else { s.putInt16(listLength + 2); // length of extension_data s.putInt16(listLength); // length of ServerNameList for (SNIServerName sniName : sniMap.values()) { s.putInt8(sniName.getType()); // server name type s.putBytes16(sniName.getEncoded()); // server name value } } }
@Override public String toString() { StringBuffer buffer = new StringBuffer(); for (SNIServerName sniName : sniMap.values()) { buffer.append("[" + sniName + "]"); } return "Extension " + type + ", server_name: " + buffer; }
static SSLClient init(String host, int port, String cipherSuiteFilter, String sniHostName) throws NoSuchAlgorithmException, IOException { SSLContext sslContext = SSLContext.getDefault(); SSLSocketFactory ssf = (SSLSocketFactory) sslContext.getSocketFactory(); SSLSocket socket = (SSLSocket) ssf.createSocket(host, port); SSLParameters params = new SSLParameters(); if (cipherSuiteFilter != null) { String[] cipherSuites = UnboundSSLUtils.filterStringArray( ssf.getSupportedCipherSuites(), cipherSuiteFilter); System.out.println("Client: enabled cipher suites: " + Arrays.toString(cipherSuites)); params.setCipherSuites(cipherSuites); } if (sniHostName != null) { System.out.println("Client: set SNI hostname: " + sniHostName); SNIHostName serverName = new SNIHostName(sniHostName); List<SNIServerName> serverNames = new ArrayList<>(); serverNames.add(serverName); params.setServerNames(serverNames); } socket.setSSLParameters(params); return new SSLClient(socket); }
@Override public String toString() { StringBuilder sb = new StringBuilder(); for (SNIServerName sniName : sniMap.values()) { sb.append("[" + sniName + "]"); } return "Extension " + type + ", server_name: " + sb; }