1 /* 2 * Copyright (c) 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. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 import java.io.IOException; 25 import java.io.InputStream; 26 import java.io.OutputStream; 27 import java.net.InetSocketAddress; 28 import java.security.cert.X509Certificate; 29 import java.util.ArrayList; 30 import java.util.List; 31 32 import javax.net.ssl.SSLContext; 33 import javax.net.ssl.SSLParameters; 34 import javax.net.ssl.SSLSession; 35 import javax.net.ssl.SSLSocket; 36 import javax.net.ssl.SSLSocketFactory; 37 38 /* 39 * A simple SSL socket client. 40 */ 41 public class Client { 42 43 private final SSLSocket socket; 44 45 public Client(SSLContext context) throws Exception { 46 SSLSocketFactory socketFactory = context.getSocketFactory(); 47 socket = (SSLSocket) socketFactory.createSocket(); 48 socket.setSoTimeout(Utils.TIMEOUT); 49 } 50 51 public Client(Cert... certs) throws Exception { 52 this(Utils.createSSLContext(certs)); 53 } 54 55 private SSLSession getSession() { 56 return socket.getSession(); 57 } 58 59 private void setEnabledCipherSuites(String... cipherSuites) { 60 socket.setEnabledCipherSuites(cipherSuites); 61 } 62 63 private void setEnabledProtocols(String... protocols) { 64 socket.setEnabledProtocols(protocols); 65 } 66 67 @SuppressWarnings(value = { "unchecked", "rawtypes" }) 68 private void setServerName(String hostname) { 69 List serverNames = new ArrayList(); 70 serverNames.add(createSNIHostName(hostname)); 71 SSLParameters params = socket.getSSLParameters(); 72 params.setServerNames(serverNames); 73 socket.setSSLParameters(params); 74 } 75 76 // Create SNIHostName via reflection due to pre-8 JDK builds don't support 77 // SNI. Those JDK builds cannot find classes SNIServerName and SNIHostName. 78 private Object createSNIHostName(String hostname) { 79 try { 80 Class<?> clazz = Class.forName("javax.net.ssl.SNIHostName"); 81 return clazz.getConstructor(String.class).newInstance(hostname); 82 } catch (Exception e) { 83 throw new RuntimeException("Creates SNIHostName failed!", e); 84 } 85 } 86 87 private void setApplicationProtocols(String... protocols) { 88 SSLParameters params = socket.getSSLParameters(); 89 params.setApplicationProtocols(protocols); 90 socket.setSSLParameters(params); 91 } 92 93 private String getNegotiatedApplicationProtocol() { 94 return socket.getApplicationProtocol(); 95 } 96 97 private void oneTimeConnect(String host, int port) throws IOException { 98 socket.connect(new InetSocketAddress(host, port)); 99 100 OutputStream out = socket.getOutputStream(); 101 out.write('C'); 102 out.flush(); 103 104 InputStream in = socket.getInputStream(); 105 in.read(); 106 } 107 108 public void close() throws IOException { 109 socket.close(); 110 } 111 112 public static void main(String[] args) throws IOException { 113 System.out.println("----- Client start -----"); 114 int port = Integer.valueOf(System.getProperty(Utils.PROP_PORT)); 115 116 String protocol = System.getProperty(Utils.PROP_PROTOCOL); 117 String cipherSuite = System.getProperty(Utils.PROP_CIPHER_SUITE); 118 String serverName = System.getProperty(Utils.PROP_SERVER_NAME); 119 String appProtocols = System.getProperty(Utils.PROP_APP_PROTOCOLS); 120 boolean supportsSNIOnServer 121 = Utils.getBoolProperty(Utils.PROP_SUPPORTS_SNI_ON_SERVER); 122 boolean supportsSNIOnClient 123 = Utils.getBoolProperty(Utils.PROP_SUPPORTS_SNI_ON_CLIENT); 124 boolean supportsALPNOnServer 125 = Utils.getBoolProperty(Utils.PROP_SUPPORTS_ALPN_ON_SERVER); 126 boolean supportsALPNOnClient 127 = Utils.getBoolProperty(Utils.PROP_SUPPORTS_ALPN_ON_CLIENT); 128 boolean negativeCase 129 = Utils.getBoolProperty(Utils.PROP_NEGATIVE_CASE_ON_CLIENT); 130 System.out.println(Utils.join(Utils.PARAM_DELIMITER, 131 "ClientJDK=" + System.getProperty(Utils.PROP_CLIENT_JDK), 132 "Protocol=" + protocol, 133 "CipherSuite=" + cipherSuite, 134 "ServerName=" + serverName, 135 "AppProtocols=" + appProtocols)); 136 137 Status status = Status.SUCCESS; 138 Client client = null; 139 try { 140 client = new Client(Cert.getCerts(cipherSuite)); 141 client.setEnabledProtocols(protocol); 142 client.setEnabledCipherSuites(cipherSuite); 143 144 if (serverName != null) { 145 if (supportsSNIOnClient) { 146 client.setServerName(serverName); 147 } else { 148 System.out.println( 149 "Ignored due to client doesn't support SNI."); 150 } 151 } 152 153 if (appProtocols != null) { 154 if (supportsALPNOnClient) { 155 client.setApplicationProtocols( 156 Utils.split(appProtocols, Utils.VALUE_DELIMITER)); 157 } else { 158 System.out.println( 159 "Ignored due to client doesn't support ALPN."); 160 } 161 } 162 163 client.oneTimeConnect("localhost", port); 164 165 if (serverName != null && supportsSNIOnServer 166 && supportsSNIOnClient) { 167 X509Certificate cert 168 = (X509Certificate) client.getSession().getPeerCertificates()[0]; 169 String subject 170 = cert.getSubjectX500Principal().getName(); 171 if (!subject.contains(serverName)) { 172 System.out.println("Unexpected server: " + subject); 173 status = Status.FAIL; 174 } 175 } 176 177 if (appProtocols != null && supportsALPNOnServer 178 && supportsALPNOnClient) { 179 String negoAppProtocol 180 = client.getNegotiatedApplicationProtocol(); 181 String expectedNegoAppProtocol 182 = System.getProperty(Utils.PROP_NEGO_APP_PROTOCOL); 183 if (!expectedNegoAppProtocol.equals(negoAppProtocol)) { 184 System.out.println("Unexpected negotiated app protocol: " 185 + negoAppProtocol); 186 status = Status.FAIL; 187 } 188 } 189 190 if (status != Status.FAIL) { 191 status = negativeCase 192 ? Status.UNEXPECTED_SUCCESS 193 : Status.SUCCESS; 194 } 195 } catch (Exception exception) { 196 status = Utils.handleException(exception, negativeCase); 197 } finally { 198 if (client != null) { 199 client.close(); 200 } 201 } 202 203 System.out.println("STATUS: " + status); 204 System.out.println("----- Client end -----"); 205 } 206 }