blob: 3a4a6cc58cbed94d9e69445a79c2d08bd29d03b9 [file] [log] [blame]
/*
* Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.SSLServerSocketFactory;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ServerSocketFactory;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import com.sun.ejte.ccl.reporter.SimpleReporterAdapter;
public class CipherTest {
private static final String DEFAULT_HOSTNAME = "localhost";
private static final int DEFAULT_PORT = 8181;
private static final String HTTP_GET_REQUEST = "GET / HTTP/1.0";
private static String serverHostname = null;
private static int serverPort = -1;
// Default and Supported chipher set
private static String[] DEFAULT_CIPHERS = null;
private static String[] SUPPORTED_CIPHERS = null;
// Flags
private static final String INTERACTIVE_OPTION = "-i";
private static final String HELP_OPTION = "-h";
private static final String DEBUG_OPTION = "-d";
private static final String VERBOSE1_OPTION = "-v";
private static final String VERBOSE2_OPTION = "-verbose";
private static final String HOST_OPTION = "-host";
private static final String PORT_OPTION = "-port";
private static final String SHOULD_PASS_OPTION = "-shouldPass";
private static final String SHOULD_FAIL_OPTION = "-shouldFail";
private static final String ENABLED_CIPHER_OPTION = "-enabledCipher";
private static boolean VERBOSE_FLAG = false;
private static void usage() {
System.out.println("usage: CipherTest [-i] [-d] [-h] [-host hostname] [-port port#] -shouldPass <comma-spa-ciphers> [-v|-verbose]");
System.out.println("-i interactive flag");
System.out.println("-d debug mode");
System.out.println("-v verbose mode");
System.out.println("-verbose verbose mode");
System.out.println("-h help/usage");
System.out.println("-host hostname - host to connect to");
System.out.println("-port port# - port to connect to");
System.out.println("-enabledCiphers - comma separated list of ciphers that should be ");
System.out.println("-shouldPass - comma separated list of ciphers that should pass");
System.out.println("-shouldFail - comma separated list of ciphers that should fail");
}
private static void verbose(String msg) {
if( VERBOSE_FLAG)
System.out.println(msg);
}
public static void main(String[] args) throws Exception {
boolean interactive = false;
boolean debug = false;
boolean help = false;
//Set<String> shouldPass = new Set<String>();
//Set<String> shouldFail = new Set<String>();
String shouldPass = null;
String shouldFail = null;
String enabledCipherAsString = null;
for(int i=0; i<args.length; i++) {
if( args[i].intern() == INTERACTIVE_OPTION.intern() ) {
interactive = true;
} else if( args[i].intern() == HELP_OPTION.intern() ) {
help = true;
} else if( args[i].intern() == VERBOSE1_OPTION.intern() ) {
VERBOSE_FLAG = true;
} else if( args[i].intern() == VERBOSE2_OPTION.intern() ) {
VERBOSE_FLAG = true;
} else if( args[i].intern() == DEBUG_OPTION.intern() ) {
debug = true;
} else if( args[i].intern() == HOST_OPTION.intern() ) {
serverHostname = args[++i];
} else if( args[i].intern() == PORT_OPTION.intern() ) {
serverPort = Integer.parseInt(args[++i]);
} else if( args[i].intern() == SHOULD_PASS_OPTION.intern() ) {
shouldPass = args[++i];
/*
* Workaround for JavaSE bug (6518827) where
* arguments of length 0 are not passed in on Windows.
*/
if (shouldPass.startsWith("-")) {
// a "" param was skipped
shouldPass = "";
i--;
}
} else if( args[i].intern() == SHOULD_FAIL_OPTION.intern() ) {
shouldFail = args[++i];
} else if( args[i].intern() == ENABLED_CIPHER_OPTION.intern() ) {
enabledCipherAsString = args[++i];
} else {
System.out.println("Unrecognized option: " + args[i]);
usage();
System.exit(10);
}
}
for(int i=0; i<args.length; i++) {
verbose("Arg[" + i + "] " + args[i]);
}
if( help) {
usage();
System.exit(0);
}
if( debug ) {
System.setProperty("javax.net.debug", "all");
}
if( shouldPass == null ) {
usage();
System.exit(11);
}
if( serverHostname == null )
serverHostname = DEFAULT_HOSTNAME;
if( serverPort == -1 )
serverPort = DEFAULT_PORT;
initCiphers();
if( interactive ) {
interactive();
} else {
nonInteractive(enabledCipherAsString, shouldPass, shouldFail);
}
}
private static void interactive()
throws IOException {
BufferedReader reader =
new BufferedReader(new InputStreamReader(System.in));
while(true) {
System.out.println();
System.out.print(
"Enter number of cipher or cipher-name to enable on this client: ");
String s = reader.readLine();
CipherTest ct = null;
try {
int num = Integer.parseInt(s);
ct = new CipherTest(num, SUPPORTED_CIPHERS[num], null);
} catch(Exception e) {
// try it as a string
ct = new CipherTest(s, s, null);
}
ct.run();
}
}
private static void nonInteractive(String enabledCipher,
String shouldPass, String shouldFail) {
// if( enabledCipher == null ) {
// for(int i=0; i<SUPPORTED_CIPHERS.length; i++) {
// CipherTest ct = new CipherTest(i, SUPPORTED_CIPHERS[i], shouldFail);
// ct.run();
// }
// } else {
// CipherTest ct = new CipherTest(enabledCipher, shouldPass, shouldFail);
// ct.run();
// }
CipherTest ct = new CipherTest(enabledCipher, shouldPass, shouldFail);
ct.run();
}
private static void nonInteractive(String[] enabledCipher,
String shouldPass, String shouldFail) {
// TODO
}
private static SimpleReporterAdapter stat =
new SimpleReporterAdapter("appserv-tests");
private int startCipher = -1;
String[] ciphers = null;
String shouldPass;
String shouldFail;
private String testId = null;
public CipherTest(int start, String shouldPass, String shouldFail) {
startCipher = start;
ciphers = new String[1];
ciphers[0] = SUPPORTED_CIPHERS[startCipher];
this.shouldPass = shouldPass;
this.shouldFail = shouldFail;
}
public CipherTest(String cipherName, String shouldPass, String shouldFail) {
if( cipherName != null ) {
ciphers = new String[1];
ciphers[0] = cipherName;
}
this.shouldPass = shouldPass;
this.shouldFail = shouldFail;
}
public void run() {
try {
testId = "SSL cipher test - " + ciphers[0];
stat.addDescription("Security::SSL cipher test " + ciphers[0]);
doSSLTest(ciphers);
verbose("Supported cipher suite: " + ciphers[0]);
passed(ciphers);
//System.exit(0);
} catch(SSLHandshakeException e) {
failed(ciphers);
verbose("Unsupported (SSLHandshakeException) ciphers: " + ciphers[0] + " ...");
} catch(SSLException e) {
failed(ciphers);
verbose("SSLException with ciphers: " + ciphers[0] + e.getMessage());
} catch(IOException e) {
failed(ciphers);
verbose("IOException with ciphers: " + ciphers[0] + " ...");
} finally {
stat.printSummary(testId);
}
}
private void passed(String[] thatPassed) {
for(int i=0; i<thatPassed.length; i++) {
int index = shouldPass.indexOf(thatPassed[i]);
if( index < 0 ) {
// is not in the should pass, test failed
System.out.println("Cipher - " + thatPassed[i] +
" - pased, but should not have. Test failed");
stat.addStatus(testId, stat.FAIL);
return;
//System.exit(1);
}
}
System.out.println("Test passed");
stat.addStatus(testId, stat.PASS);
//System.exit(0);
}
private void failed(String[] thatFailed) {
for(int i=0; i<thatFailed.length; i++) {
int index = shouldPass.indexOf(thatFailed[i]);
if( index >= 0 ) {
// is in the should pass, but did not
System.out.println("Cipher - " + thatFailed[i] +
" - failed, but should have passed. Test failed");
stat.addStatus(testId, stat.FAIL);
return;
//System.exit(1);
}
}
System.out.println("(Negative) Test passed");
stat.addStatus(testId, stat.PASS);
//System.exit(0);
}
private void doSSLTest(String[] enableCiphers) throws IOException {
SSLSocketFactory sslSocketFactory =
(SSLSocketFactory)SSLSocketFactory.getDefault();
SSLSocket secureSocket = (SSLSocket)
sslSocketFactory.createSocket(serverHostname, serverPort);
if( enableCiphers != null )
secureSocket.setEnabledCipherSuites(enableCiphers);
BufferedWriter buffWriter = new BufferedWriter(
new OutputStreamWriter(secureSocket.getOutputStream()));
buffWriter.write(HTTP_GET_REQUEST);
buffWriter.newLine();
buffWriter.newLine();
buffWriter.flush();
//buffWriter.close();
//System.out.println("Written request to server: " + HTTP_GET_REQUEST);
BufferedReader buffReader = new BufferedReader(
new InputStreamReader(secureSocket.getInputStream()));
String readin = null;
while( (readin=buffReader.readLine()) != null ) {
//System.out.println(readin);
}
}
private static void initCiphers() {
ServerSocketFactory ssf = SSLServerSocketFactory.getDefault();
SSLServerSocketFactory sslssf = null;
if( ssf instanceof SSLServerSocketFactory ) {
sslssf = (SSLServerSocketFactory)ssf;
} else {
System.out.println(ssf.getClass().getName());
System.exit(1);
}
DEFAULT_CIPHERS = sslssf.getDefaultCipherSuites();
SUPPORTED_CIPHERS = sslssf.getSupportedCipherSuites();
for(int i=0; i<DEFAULT_CIPHERS.length; i++) {
verbose("Default cipher[" + i + "] "+DEFAULT_CIPHERS[i]);
}
for(int i=0; i<SUPPORTED_CIPHERS.length; i++) {
verbose("Supported cipher[" + i + "] " + SUPPORTED_CIPHERS[i]);
}
}
}