Skip to content

Commit

Permalink
Add TLS / SSL functional tests (#2049)
Browse files Browse the repository at this point in the history
Exercises the TLS/SSL stack by calling websites to ensure that TLS/SSL work and a functional trust store is present.

Fixes #1963.

Signed-off-by: Shelley Lambert <slambert@gmail.com>
Co-authored-by: Andreas Ahlenstorf <andreas@ahlenstorf.ch>
  • Loading branch information
smlambert and aahlenst authored Nov 12, 2020
1 parent c6f016d commit 51df59f
Show file tree
Hide file tree
Showing 6 changed files with 525 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ autoGen.mk
TestConfig
TKG
functional
!functional/security
!functional/SyntheticGCWorkload
!functional/MBCS_Tests

Expand Down
71 changes: 71 additions & 0 deletions functional/security/build.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?xml version="1.0"?>

<!--
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-->

<project name="Security Functional tests" default="build" basedir=".">
<taskdef resource="net/sf/antcontrib/antlib.xml" />
<description>
Security Functional tests
</description>
<import file="${TEST_ROOT}/functional/build.xml"/>

<!-- set global properties for this build -->
<property name="DEST" value="${BUILD_ROOT}/functional/security" />

<!--Properties for this particular build-->
<property name="src" location="./src" />
<property name="build" location="./bin" />

<target name="init">
<mkdir dir="${DEST}" />
<mkdir dir="${build}" />
</target>

<target name="compile" depends="init" description="Using java ${JDK_VERSION} to compile the source ">
<echo>Ant version is ${ant.version}</echo>
<echo>============COMPILER SETTINGS============</echo>
<echo>===fork: yes</echo>
<echo>===executable: ${compiler.javac}</echo>
<echo>===debug: on</echo>
<echo>===destdir: ${DEST}</echo>
<javac srcdir="${src}" destdir="${build}" debug="true" fork="true" executable="${compiler.javac}" includeAntRuntime="false" encoding="ISO-8859-1">
<src path="${src}" />
<classpath>
<pathelement location="${LIB_DIR}/testng.jar"/>
<pathelement location="${LIB_DIR}/jcommander.jar"/>
</classpath>
</javac>
</target>

<target name="dist" depends="compile,dist_functional" description="generate the distribution">
<jar jarfile="${DEST}/SecurityTests.jar" filesonly="true">
<fileset dir="${build}" />
<fileset dir="${src}/../" includes="*.properties,*.xml" />
</jar>
<copy todir="${DEST}">
<fileset dir="${src}/../" includes="*.xml" />
<fileset dir="${src}/../" includes="*.mk" />
</copy>
</target>

<target name="clean" depends="dist" description="clean up">
<!-- Delete the ${build} directory trees -->
<delete dir="${build}" />
</target>

<target name="build" >
<antcall target="clean" inheritall="true" />
</target>
</project>
28 changes: 28 additions & 0 deletions functional/security/playlist.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?xml version='1.0' encoding='UTF-8'?>
<!--
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
-->

<playlist xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../TKG/playlist.xsd">
<test>
<testCaseName>SecurityTests</testCaseName>
<command>$(JAVA_COMMAND) $(JVM_OPTIONS) -cp $(Q)$(RESOURCES_DIR)$(P)$(TESTNG)$(P)$(TEST_RESROOT)$(D)SecurityTests.jar$(Q) org.testng.TestNG $(Q)$(TEST_RESROOT)$(D)testng.xml$(Q) -d $(REPORTDIR) -testnames SecurityTests -groups $(TEST_GROUP) -excludegroups $(DEFAULT_EXCLUDE); \
$(TEST_STATUS)</command>
<levels>
<level>extended</level>
</levels>
<groups>
<group>functional</group>
</groups>
</test>
</playlist>
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package net.adoptopenjdk.test;

import org.testng.Assert;
import org.testng.annotations.Test;
import org.testng.log4testng.Logger;
import javax.net.ssl.HandshakeCompletedEvent;
import javax.net.ssl.HandshakeCompletedListener;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.UnknownHostException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

/**
* Creates actual HTTPS connections to real webservers to exercise the TLS stack.
*/
@Test(groups={ "level.extended" })
public class DefaultTlsFunctionalTest {

private static Logger logger = Logger.getLogger(DefaultTlsFunctionalTest.class);

private final static String[] TLS_HOSTS = {
"https://www.cloudflare.com/",
"https://www.google.com/",
"https://services.gradle.org/", // Ensure that Gradle can be downloaded.
"https://repo1.maven.org/" // Ensure that Maven artifacts can be resolved.
};

@Test
public void testDefaultTls() throws IOException, NoSuchAlgorithmException {
String versionString = System.getProperty("java.version");
logger.info("running DefaultTlsFunctionalTest");
logger.info("Running on Java version: " + versionString);

SSLContext sc = SSLContext.getDefault();

int unreachableCounter = 0;
HttpsURLConnection con = null;
try {
for (String host : TLS_HOSTS) {
SSLSocketFactory sslSocketFactory = new DecoratedSSLSocketFactory(sc.getSocketFactory(),
new SSLSessionExaminingHandshakeListener(host));

URL url = new URL(host);
con = (HttpsURLConnection) url.openConnection();
con.setSSLSocketFactory(sslSocketFactory);

// Servers can be unreachable. Fail later if we cannot reach any.
try {
con.connect();
} catch (UnknownHostException | SocketTimeoutException ex) {
unreachableCounter += 1;
ex.printStackTrace(System.err);
continue;
}
}
} finally {
if (con != null) {
con.disconnect();
}
}

// Fail if we could not reach any host because this means we cannot say
// anything about the state of the TLS stack.
if (unreachableCounter == TLS_HOSTS.length) {
Assert.fail("Could not reach any host");
}
}

static class SSLSessionExaminingHandshakeListener implements HandshakeCompletedListener {

private final String host;

public SSLSessionExaminingHandshakeListener(String host) {
this.host = host;
}

@Override
public void handshakeCompleted(HandshakeCompletedEvent event) {
logger.info("Connected to: " + this.host);
try {
logger.info(event.getPeerPrincipal().getName());
} catch (SSLPeerUnverifiedException e) {
throw new RuntimeException(e);
}

logger.info("Protocol: " + event.getSession().getProtocol());
logger.info("Supported cipher suites: " +
Arrays.toString(event.getSocket().getSupportedCipherSuites()));
logger.info("Enabled cipher suites: " + Arrays.toString(event.getSocket().getEnabledCipherSuites()));
logger.info("Selected cipher suite: " + event.getSession().getCipherSuite());
}
}

static class DecoratedSSLSocketFactory extends SSLSocketFactory {

private final SSLSocketFactory delegate;

private final HandshakeCompletedListener handshakeCompletedListener;

DecoratedSSLSocketFactory(SSLSocketFactory delegate, HandshakeCompletedListener handshakeCompletedListener) {
this.delegate = delegate;
this.handshakeCompletedListener = handshakeCompletedListener;
}

@Override
public String[] getDefaultCipherSuites() {
return this.delegate.getDefaultCipherSuites();
}

@Override
public String[] getSupportedCipherSuites() {
return this.delegate.getSupportedCipherSuites();
}

@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
SSLSocket socket = (SSLSocket) this.delegate.createSocket(s, host, port, autoClose);
socket.addHandshakeCompletedListener(this.handshakeCompletedListener);
return socket;
}

@Override
public Socket createSocket(String host, int port) throws IOException {
SSLSocket socket = (SSLSocket) this.delegate.createSocket(host, port);
socket.addHandshakeCompletedListener(this.handshakeCompletedListener);
return socket;
}

@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
SSLSocket socket = (SSLSocket) this.delegate.createSocket(host, port, localHost, localPort);
socket.addHandshakeCompletedListener(this.handshakeCompletedListener);
return socket;
}

@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
SSLSocket socket = (SSLSocket) this.delegate.createSocket(host, port);
socket.addHandshakeCompletedListener(this.handshakeCompletedListener);
return socket;
}

@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort)
throws IOException {
SSLSocket socket = (SSLSocket) this.delegate.createSocket(address, port, localAddress, localPort);
socket.addHandshakeCompletedListener(this.handshakeCompletedListener);
return socket;
}
}
}
Loading

0 comments on commit 51df59f

Please sign in to comment.