Skip to content

Commit

Permalink
Support of TLSv1.3 by JDK Client (#707)
Browse files Browse the repository at this point in the history
Support TLSv1.3

Signed-off-by: Jorge Bescos Gascon <jorge.bescos.gascon@oracle.com>
  • Loading branch information
jbescos authored and jansupol committed Jul 8, 2020
1 parent 7f83e58 commit 41ab439
Show file tree
Hide file tree
Showing 11 changed files with 163 additions and 79 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2017 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2020 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
Expand Down Expand Up @@ -62,6 +62,7 @@ class SslFilter extends Filter {
/* Some operations on SSL engine require a buffer as a parameter even if they don't need any data.
This buffer is for that purpose. */
private static final ByteBuffer emptyBuffer = ByteBuffer.allocate(0);
private static final String TLSV13 = "TLSv1.3";

// buffer for passing data to the upper filter
private final ByteBuffer applicationInputBuffer;
Expand All @@ -73,6 +74,7 @@ class SslFilter extends Filter {
private final WriteQueue writeQueue = new WriteQueue();

private volatile State state = State.NOT_STARTED;
private volatile boolean tlsv13 = false;
/*
* Pending write operation stored when writing data was not possible. It will be resumed when write operation is
* available again. Only one write operation can be in progress at a time. Trying to store more than one pending
Expand Down Expand Up @@ -193,14 +195,14 @@ BUFFER_UNDERFLOW can occur only after unwrap(), but to be 100% sure we handle al
}

case CLOSED: {
state = State.CLOSED;
setState(State.CLOSED);
break;
}

case OK: {
// check if we started re-handshaking
if (result.getHandshakeStatus() != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
state = State.REHANDSHAKING;
if (isHandshaking(result.getHandshakeStatus())) {
setState(State.REHANDSHAKING);
}

((Buffer) networkOutputBuffer).flip();
Expand Down Expand Up @@ -362,7 +364,6 @@ private boolean handleRead(ByteBuffer networkData) {
try {
((Buffer) applicationInputBuffer).clear();
SSLEngineResult result = sslEngine.unwrap(networkData, applicationInputBuffer);

switch (result.getStatus()) {
case BUFFER_OVERFLOW: {
/* This means that the content of the ssl packet (max 16kB) did not fit into
Expand Down Expand Up @@ -392,12 +393,11 @@ private boolean handleRead(ByteBuffer networkData) {
// signal that there is nothing useful left in this buffer
return false;
}

// we started re-handshaking
if (result.getHandshakeStatus() != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING
if (!tlsv13 && isHandshaking(result.getHandshakeStatus())
// make sure we don't confuse re-handshake with closing handshake
&& !sslEngine.isOutboundDone()) {
state = State.REHANDSHAKING;
setState(State.REHANDSHAKING);
return doHandshakeStep(networkData);
}

Expand All @@ -419,19 +419,18 @@ private boolean doHandshakeStep(ByteBuffer networkData) {
boolean handshakeFinished = false;

synchronized (this) {
if (SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING.equals(sslEngine.getHandshakeStatus())) {
SSLEngineResult.HandshakeStatus hs = sslEngine.getHandshakeStatus();
if (!isHandshaking(hs)) {
// we stopped handshaking while waiting for the lock
return true;
}

try {
/* we don't use networkOutputBuffer, because there might be a write operation still in progress ->
we don't want to corrupt the buffer it is using */
LazyBuffer outputBuffer = new LazyBuffer();
boolean stepFinished = false;
while (!stepFinished) {
SSLEngineResult.HandshakeStatus hs = sslEngine.getHandshakeStatus();

hs = sslEngine.getHandshakeStatus();
switch (hs) {
case NOT_HANDSHAKING: {
/* This should never happen. If we are here and not handshaking, it means a bug
Expand All @@ -441,25 +440,18 @@ private boolean doHandshakeStep(ByteBuffer networkData) {
throw new IllegalStateException("Trying to handshake, but SSL engine not in HANDSHAKING state."
+ "SSL filter state: \n" + getDebugState());
}

case FINISHED: {
/* According to SSLEngine javadoc FINISHED status can be returned only in SSLEngineResult,
but just to make sure we don't end up in an infinite loop when presented with an SSLEngine
implementation that does not respect this:*/
stepFinished = true;
handshakeFinished = true;
break;
throw new IllegalStateException("Trying to handshake, but SSL engine not in HANDSHAKING state."
+ "SSL filter state: \n" + getDebugState());
}
// needs to write data to the network
case NEED_WRAP: {
ByteBuffer byteBuffer = outputBuffer.get();
SSLEngineResult result = sslEngine.wrap(emptyBuffer, byteBuffer);

if (result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.FINISHED) {
stepFinished = true;
handshakeFinished = true;
}

switch (result.getStatus()) {
case BUFFER_OVERFLOW: {
outputBuffer.resize();
Expand All @@ -476,7 +468,7 @@ that BUFFER_UNDERFLOW can occur only after unwrap(), but to be 100% sure we hand

case CLOSED: {
stepFinished = true;
state = State.CLOSED;
setState(State.CLOSED);
break;
}
}
Expand Down Expand Up @@ -517,7 +509,7 @@ that BUFFER_UNDERFLOW can occur only after unwrap(), but to be 100% sure we hand

case CLOSED: {
stepFinished = true;
state = State.CLOSED;
setState(State.CLOSED);
break;
}
}
Expand Down Expand Up @@ -559,6 +551,7 @@ that BUFFER_UNDERFLOW can occur only after unwrap(), but to be 100% sure we hand

if (handshakeFinished) {
handleHandshakeFinished();
tlsv13 = TLSV13.equals(sslEngine.getSession().getProtocol());
// indicate that there still might be usable data in the input buffer
return true;
}
Expand All @@ -576,12 +569,11 @@ private void handleHandshakeFinished() {
return;
}


if (state == State.HANDSHAKING) {
state = State.DATA;
setState(State.DATA);
upstreamFilter.onSslHandshakeCompleted();
} else if (state == State.REHANDSHAKING) {
state = State.DATA;
setState(State.DATA);
if (pendingApplicationWrite != null) {
Runnable write = pendingApplicationWrite;
// set pending write to null to cover the extremely improbable case that we start re-handshaking again
Expand All @@ -599,7 +591,7 @@ private void handleSslError(Throwable t) {
@Override
void startSsl() {
try {
state = State.HANDSHAKING;
setState(State.HANDSHAKING);
sslEngine.beginHandshake();
doHandshakeStep(emptyBuffer);
} catch (SSLException e) {
Expand Down Expand Up @@ -741,4 +733,14 @@ public String toString() {
}
}
}

private void setState(State state) {
this.state = state;
}

private boolean isHandshaking(SSLEngineResult.HandshakeStatus hs) {
return SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING != hs
// TLSv1.3 introduces this, and it is considered as not handshaking
&& SSLEngineResult.HandshakeStatus.FINISHED != hs;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@

package org.glassfish.tyrus.container.jdk.client;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
Expand All @@ -42,29 +46,28 @@
import org.glassfish.tyrus.client.SslEngineConfigurator;
import org.glassfish.tyrus.client.ThreadPoolConfig;
import org.glassfish.tyrus.spi.CompletionHandler;

import org.junit.Before;
import org.junit.Test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

/**
* @author Petr Janouch
*/

public class SslFilterTest {

private static final int PORT = 8321;
protected static final int PORT = 8321;
protected static final String SERVER_KEY_STORE = "/keystore_server";
protected static final String SERVER_TRUST_STORE = "/truststore_server";
protected static final String CLIENT_KEY_STORE = "/keystore_client";
protected static final String CLIENT_TRUST_STORE = "/truststore_client";
protected static final String PASSWORD = "asdfgh";

@Before
public void beforeTest() {
System.setProperty("javax.net.ssl.keyStore", this.getClass().getResource("/keystore_server").getPath());
System.setProperty("javax.net.ssl.keyStorePassword", "asdfgh");
System.setProperty("javax.net.ssl.trustStore", this.getClass().getResource("/truststore_server").getPath());
System.setProperty("javax.net.ssl.trustStorePassword", "asdfgh");
System.setProperty("jdk.tls.server.protocols", "TLSv1.2");
System.setProperty("jdk.tls.client.protocols", "TLSv1.2");
public void before() {
System.setProperty("javax.net.ssl.keyStore", this.getClass().getResource(SERVER_KEY_STORE).getPath());
System.setProperty("javax.net.ssl.keyStorePassword", PASSWORD);
System.setProperty("javax.net.ssl.trustStore", this.getClass().getResource(SERVER_TRUST_STORE).getPath());
System.setProperty("javax.net.ssl.trustStorePassword", PASSWORD);
}

@Test
Expand Down Expand Up @@ -376,10 +379,10 @@ private ByteBuffer stringToBuffer(String string) {
private Filter openClientSocket(String host, final ByteBuffer readBuffer, final CountDownLatch completionLatch,
HostnameVerifier customHostnameVerifier) throws Throwable {
SslContextConfigurator sslConfig = SslContextConfigurator.DEFAULT_CONFIG;
sslConfig.setTrustStoreFile(this.getClass().getResource("/truststore_client").getPath())
.setTrustStorePassword("asdfgh")
.setKeyStoreFile(this.getClass().getResource("/keystore_client").getPath())
.setKeyStorePassword("asdfgh");
sslConfig.setTrustStoreFile(this.getClass().getResource(CLIENT_TRUST_STORE).getPath())
.setTrustStorePassword(PASSWORD)
.setKeyStoreFile(this.getClass().getResource(CLIENT_KEY_STORE).getPath())
.setKeyStorePassword(PASSWORD);
SslEngineConfigurator sslEngineConfigurator = new SslEngineConfigurator(sslConfig.createSSLContext());
sslEngineConfigurator.setHostnameVerifier(customHostnameVerifier);

Expand Down Expand Up @@ -461,7 +464,6 @@ void close() {
clientSocket.close();
throw exception.get();
}

return clientSocket;
}

Expand Down
17 changes: 0 additions & 17 deletions containers/jdk-client/src/test/resources/client.cert

This file was deleted.

27 changes: 27 additions & 0 deletions containers/jdk-client/src/test/resources/clientkey.cert
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
Alias name: clientkey
Creation date: Jun 22, 2020
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:
-----BEGIN CERTIFICATE-----
MIIDjTCCAnWgAwIBAgIEdX5IhDANBgkqhkiG9w0BAQsFADB2MQswCQYDVQQGEwJD
WjEXMBUGA1UECBMOQ3plY2ggUmVwdWJsaWMxDzANBgNVBAcTBlByYWd1ZTEbMBkG
A1UEChMST3JhY2xlIENvcnBvcmF0aW9uMQ8wDQYDVQQLEwZKZXJzZXkxDzANBgNV
BAMTBkNsaWVudDAgFw0yMDA2MjIwNzI5MzZaGA8yMTIwMDUyOTA3MjkzNlowdjEL
MAkGA1UEBhMCQ1oxFzAVBgNVBAgTDkN6ZWNoIFJlcHVibGljMQ8wDQYDVQQHEwZQ
cmFndWUxGzAZBgNVBAoTEk9yYWNsZSBDb3Jwb3JhdGlvbjEPMA0GA1UECxMGSmVy
c2V5MQ8wDQYDVQQDEwZDbGllbnQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
AoIBAQClVUtMXL/+WK5Ma6mV+BeGZlSlGqT9teUUDwPkipZMA9wdXTC3iSIp098s
NCiPJBPXs+0KSrrPVgcHlMONcAzg3FHQrbP1aIH0oFOHR1bF/KNG1/XzV8HLMHqM
ynnDJ69sYOUy6BO6lYQM1GaPf0fdqkdii2/clKiKzzT/Ohr6549q3vdzHw4hKmoE
SKrdmv4OkOFGKzAH2dzNciHGnpiQj03DhjAN/8KobdUbsxoklnYOdDkg2UErI0zC
qPRg2DVVOxhViMOmPBheXVvdDiIAdLlXRiPTraetr1KxDHV6KXsQhKmADZhIj8jY
NjIyMo52bRTToaJQL5DKF6Boyx/tAgMBAAGjITAfMB0GA1UdDgQWBBSjbPqA6Tkv
6oS99eZLNQk11M+DODANBgkqhkiG9w0BAQsFAAOCAQEACNAqN0lz1cUlIocfhSk8
JlAHsRbWoCxxJZNJE0WWZ6lfH5xdlW0/87yfSvtLwDOY/8OYAhbzjG5O8mQv/I32
b2Acs42Sh+otVyLjbYicv+1yMuvmBnhEImeMHSGFjczYf9zQ+2PlerxdNwVKdpUL
V3Dt68wTlWNRYm9X6uVpNRG7fy7/goeJhAXuVER9Gtl/LQ6GJyZjVxYemckoXWDY
DCqVlzsZpnyoI2lrYXlcT3liPlRLJjqnWvmmF9GyqiIVeng9VTfStsEQ6LUfwKQO
dtvwYhbVtL++fuX/99WYCDRL8mSFu8REruQln3TJf79wOQ14O0H5jWjr4QoeOIf9
aw==
-----END CERTIFICATE-----
Binary file modified containers/jdk-client/src/test/resources/keystore_client
Binary file not shown.
Binary file modified containers/jdk-client/src/test/resources/keystore_server
Binary file not shown.
60 changes: 60 additions & 0 deletions containers/jdk-client/src/test/resources/regenerate.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Copyright (c) 2020 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

# Use this script to easily generate new keystores and truststores for the tests.
# It must be executed in the same folder this script exists.

#!/bin/bash

set -e

FOLDER=./
PASSWORD='asdfgh'

function clean(){
echo "=============================> Cleaning"
cd ${FOLDER}
git clean -df $FOLDER && git checkout $FOLDER && git status
}

function printKeystore(){
KEYSTORE=$1
echo "=============================> Print ${KEYSTORE}"
keytool -list -keystore ${FOLDER}/${KEYSTORE} -storepass ${PASSWORD} -v
}

function createKeystore(){
NEW_ALIAS=$1
KEYSTORE=$2
DNAME=$3
NEW_KEYSTORE=${KEYSTORE}
keytool -keystore ${FOLDER}/${NEW_KEYSTORE} -genkey -keyalg RSA -alias ${NEW_ALIAS} -storepass ${PASSWORD} -dname "${DNAME}" -validity 36500
keytool -list -rfc -keystore ${FOLDER}/${NEW_KEYSTORE} -alias ${NEW_ALIAS} -storepass ${PASSWORD} > ${FOLDER}/${NEW_ALIAS}.cert
echo "=============================> ${FOLDER}/${OLD_KEYSTORE} was created"
}

function createTruststore(){
NEW_ALIAS=$1
TRUSTSTORE=$2
CERT=$3
keytool -import -noprompt -file ${FOLDER}/${CERT} -alias ${NEW_ALIAS} -keystore ${FOLDER}/${TRUSTSTORE} -storepass ${PASSWORD}
echo "=============================> ${FOLDER}/${TRUSTSTORE} was created"
}

clean

createKeystore 'serverkey' 'keystore_server.new' 'CN=localhost, OU=Jersey, O=Oracle Corporation, L=Prague, ST=Czech Republic, C=CZ'
createKeystore 'clientkey' 'keystore_client.new' 'CN=Client, OU=Jersey, O=Oracle Corporation, L=Prague, ST=Czech Republic, C=CZ'
createTruststore 'clientcert' 'truststore_server.new' 'clientkey.cert'
createTruststore 'servercert' 'truststore_client.new' 'serverkey.cert'
17 changes: 0 additions & 17 deletions containers/jdk-client/src/test/resources/server.cert

This file was deleted.

Loading

0 comments on commit 41ab439

Please sign in to comment.