Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exposing the updateSSLOptions method of the NetServer class #138

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion vertx-proton/src/main/java/io/vertx/proton/ProtonServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@
import io.vertx.core.AsyncResult;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import io.vertx.proton.sasl.ProtonSaslAuthenticatorFactory;
import io.vertx.core.net.ServerSSLOptions;
import io.vertx.proton.impl.ProtonServerImpl;
import io.vertx.proton.sasl.ProtonSaslAuthenticatorFactory;

/**
* @author <a href="http://tfox.org">Tim Fox</a>
Expand Down Expand Up @@ -143,6 +144,15 @@ static ProtonServer create(Vertx vertx, ProtonServerOptions options) {
*/
ProtonServer listen();

/**
* Update the server with new SSL options, the update happens if the options object is valid and different from the existing options object.
*
* @param options the options to use
* @param force force the update when options are equals
* @param handler the result handler
*/
void updateSSLOptions(ServerSSLOptions options, boolean force, Handler<AsyncResult<ProtonServer>> handler);

/**
* Closes the server and any currently open connections. May not complete until after method has returned.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import io.vertx.core.internal.ContextInternal;
import io.vertx.core.net.NetServer;
import io.vertx.core.net.NetSocket;
import io.vertx.core.net.ServerSSLOptions;
import io.vertx.proton.ProtonConnection;
import io.vertx.proton.ProtonServer;
import io.vertx.proton.ProtonServerOptions;
Expand Down Expand Up @@ -118,6 +119,18 @@ public ProtonServerImpl listen(int i, Handler<AsyncResult<ProtonServer>> handler
return this;
}

@Override
public void updateSSLOptions(ServerSSLOptions options, boolean force, Handler<AsyncResult<ProtonServer>> handler) {
server.updateSSLOptions(options, force).onComplete(result -> {
if (result.succeeded()) {
handler.handle(Future.succeededFuture(ProtonServerImpl.this));
} else {
handler.handle(Future.failedFuture(result.cause()));
}
}
);
}

@Override
public void close() {
server.close();
Expand Down
127 changes: 110 additions & 17 deletions vertx-proton/src/test/java/io/vertx/tests/ProtonClientSslTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,38 +15,42 @@
*/
package io.vertx.tests;

import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.security.KeyStore;
import java.util.concurrent.ExecutionException;

import io.netty.handler.ssl.ApplicationProtocolConfig;
import io.netty.handler.ssl.IdentityCipherSuiteFilter;
import io.netty.handler.ssl.JdkSslContext;
import io.vertx.core.net.JdkSSLEngineOptions;
import io.vertx.core.spi.tls.SslContextFactory;
import io.vertx.proton.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import io.vertx.core.AsyncResult;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import io.vertx.core.http.ClientAuth;
import io.vertx.core.internal.logging.Logger;
import io.vertx.core.internal.logging.LoggerFactory;
import io.vertx.core.net.JdkSSLEngineOptions;
import io.vertx.core.net.PfxOptions;
import io.vertx.core.net.ServerSSLOptions;
import io.vertx.core.spi.tls.SslContextFactory;
import io.vertx.ext.unit.Async;
import io.vertx.ext.unit.TestContext;
import io.vertx.ext.unit.junit.VertxUnitRunner;
import io.vertx.proton.ProtonClient;
import io.vertx.proton.ProtonClientOptions;
import io.vertx.proton.ProtonConnection;
import io.vertx.proton.ProtonReceiver;
import io.vertx.proton.ProtonServer;
import io.vertx.proton.ProtonServerOptions;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.security.KeyStore;
import java.util.concurrent.ExecutionException;

@RunWith(VertxUnitRunner.class)
public class ProtonClientSslTest {
Expand All @@ -57,6 +61,8 @@ public class ProtonClientSslTest {
private static final String KEYSTORE = "src/test/resources/broker-pkcs12.keystore";
private static final String WRONG_HOST_KEYSTORE = "src/test/resources/broker-wrong-host-pkcs12.keystore";
private static final String TRUSTSTORE = "src/test/resources/client-pkcs12.truststore";
private static final String KEYSTORE_UPDATED = "src/test/resources/broker-updated-pkcs12.keystore";
private static final String TRUSTSTORE_UPDATED = "src/test/resources/client-updated-pkcs12.truststore";
private static final String KEYSTORE_CLIENT = "src/test/resources/client-pkcs12.keystore";
private static final String OTHER_CA_TRUSTSTORE = "src/test/resources/other-ca-pkcs12.truststore";
private static final String VERIFY_HTTPS = "HTTPS";
Expand Down Expand Up @@ -381,9 +387,96 @@ private void doHostnameVerificationTestImpl(TestContext context, boolean verifyH
async.awaitSuccess();
}

/**
* This test is here to cover update server SSL Options. Historical connections are not affected.
* New connections are successfully connected using the new trustStore.
*/
@Test(timeout = 20000)
public void testUpdateSSLOptionsSuccess(TestContext context) throws Exception {
doTestUpdateSSLOptions(context, true);
}

@Test(timeout = 20000)
public void testUpdateSSLOptionsNewConnectionWithOldTrustStoreFail(TestContext context) throws Exception {
doTestUpdateSSLOptions(context, false);
}

private void doTestUpdateSSLOptions(TestContext context, boolean isClientUsingNewTrustStore) throws Exception {
Async async = context.async();

// Create a server that accept a connection and expects a client connection+session+receiver
ProtonServerOptions serverOptions = new ProtonServerOptions();
serverOptions.setSsl(true);
PfxOptions serverPfxOptions = new PfxOptions().setPath(KEYSTORE).setPassword(PASSWORD);
serverOptions.setKeyCertOptions(serverPfxOptions);

protonServer = createServer(serverOptions, this::handleClientConnectionSessionReceiverOpen);

// Connect the client and open a receiver to verify the connection works
ProtonClientOptions clientOptions = createClientOptionsByTrustStorePath(TRUSTSTORE);

ProtonClient client = ProtonClient.create(vertx);
client.connect(clientOptions, "localhost", protonServer.actualPort(), context.asyncAssertSuccess(connection -> {
// Don't expect the connection disconnect when update server ssl options
connection.disconnectHandler(protonConnection -> {
if (!async.isCompleted()) {
context.fail("connection close");
}
}).open();

ProtonReceiver receiver = connection.createReceiver("some-address");

receiver.openHandler(context.asyncAssertSuccess(recv -> {
LOG.trace("Client receiver open");
ServerSSLOptions serverSSLOptions = new ServerSSLOptions();
PfxOptions serverPfxOptionsUpdated = new PfxOptions().setPath(KEYSTORE_UPDATED).setPassword(PASSWORD);
serverSSLOptions.setKeyCertOptions(serverPfxOptionsUpdated);
protonServer.updateSSLOptions(serverSSLOptions, false, context.asyncAssertSuccess(server -> {
if (isClientUsingNewTrustStore) {
// the connection is successfully connected using new truestStore
createNewClientByTrustStorePath(client, async, context, TRUSTSTORE_UPDATED, true);
} else {
// the connection is fails to connected using old trustStore
createNewClientByTrustStorePath(client, async, context, TRUSTSTORE, false);
}
}));
})).closeHandler(protonReceiver -> context.fail("receiver close")).open();
}));

async.awaitSuccess();
}

private void createNewClientByTrustStorePath(ProtonClient client, Async async, TestContext context, String trustStorePath, boolean expectSuccess) {
if (!expectSuccess) {
client.connect(createClientOptionsByTrustStorePath(trustStorePath), "localhost", protonServer.actualPort(),
context.asyncAssertFailure(connection -> async.complete()));
return;
}

client.connect(createClientOptionsByTrustStorePath(trustStorePath), "localhost", protonServer.actualPort(),
context.asyncAssertSuccess(connection -> {
connection.open();

ProtonReceiver receiver = connection.createReceiver("some-address");

receiver.openHandler(context.asyncAssertSuccess(rece -> {
LOG.trace("Client receiver open");
async.complete();
})).open();
}));
}

private ProtonClientOptions createClientOptionsByTrustStorePath(String trustStorePath) {
ProtonClientOptions clientOptions = new ProtonClientOptions();
clientOptions.setSsl(true);
PfxOptions clientPfxOptions = new PfxOptions().setPath(trustStorePath).setPassword(PASSWORD);
clientOptions.setTrustOptions(clientPfxOptions);
return clientOptions;
}

private ProtonServer createServer(ProtonServerOptions serverOptions,
Handler<ProtonConnection> serverConnHandler) throws InterruptedException,
ExecutionException {
ExecutionException {
ProtonServer server = ProtonServer.create(vertx, serverOptions);

server.connectHandler(serverConnHandler);
Expand Down
17 changes: 17 additions & 0 deletions vertx-proton/src/test/resources/README.txt
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,20 @@ keytool -storetype pkcs12 -keystore ca-pkcs12.keystore -storepass password -alia

keytool -storetype pkcs12 -keystore broker-wrong-host-pkcs12.keystore -storepass password -keypass password -importcert -alias ca -file ca.crt -noprompt
keytool -storetype pkcs12 -keystore broker-wrong-host-pkcs12.keystore -storepass password -keypass password -importcert -alias broker-wrong-host -file broker-wrong-host.crt

# Create updated key pair for the broker, and sign it with the CA:
# --------------------------------------------------------------------------------------
keytool -storetype pkcs12 -keystore ca-updated-pkcs12.keystore -storepass password -keypass password -alias ca-updated -genkey -keyalg "RSA" -keysize 2048 -dname "O=My Trusted Inc.,CN=my-vertx-ca.org" -validity 9999 -ext bc:c=ca:true
keytool -storetype pkcs12 -keystore ca-updated-pkcs12.keystore -storepass password -alias ca-updated -exportcert -rfc > ca-updated.crt

keytool -storetype pkcs12 -keystore broker-updated-pkcs12.keystore -storepass password -keypass password -alias broker-updated -genkey -keyalg "RSA" -keysize 2048 -dname "O=Server,CN=localhost" -validity 9999 -ext bc=ca:false -ext eku=sA

keytool -storetype pkcs12 -keystore broker-updated-pkcs12.keystore -storepass password -alias broker-updated -certreq -file broker-updated.csr
keytool -storetype pkcs12 -keystore ca-updated-pkcs12.keystore -storepass password -alias ca-updated -gencert -rfc -infile broker-updated.csr -outfile broker-updated.crt -validity 9999 -ext bc=ca:false -ext eku=sA

keytool -storetype pkcs12 -keystore broker-updated-pkcs12.keystore -storepass password -keypass password -importcert -alias ca-updated -file ca-updated.crt -noprompt
keytool -storetype pkcs12 -keystore broker-updated-pkcs12.keystore -storepass password -keypass password -importcert -alias broker-updated -file broker-updated.crt

# Create updated trust store for the client, import the CA cert:
# -------------------------------------------------------
keytool -storetype pkcs12 -keystore client-updated-pkcs12.truststore -storepass password -keypass password -importcert -alias ca-updated -file ca-updated.crt -noprompt
Binary file not shown.
20 changes: 20 additions & 0 deletions vertx-proton/src/test/resources/broker-updated.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDOzCCAiOgAwIBAgIEBDbkcjANBgkqhkiG9w0BAQsFADA0MRgwFgYDVQQDEw9t
eS12ZXJ0eC1jYS5vcmcxGDAWBgNVBAoTD015IFRydXN0ZWQgSW5jLjAgFw0yNDEx
MTUxNjI0MjZaGA8yMDUyMDQwMTE2MjQyNlowJTESMBAGA1UEAxMJbG9jYWxob3N0
MQ8wDQYDVQQKEwZTZXJ2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
AQCq3lbL8oRXdirtReK/j48pXhj4nrTMCDncZfS21RymC6h6AHrWryMxJGdbef+J
l5TIz9+FQ5fHXMCrYAjBfAzXOOPNIdHULWFbtFYvMBYt465rce6a9Cx+oLMzXF/d
wEyp0UUw1qfohIHQXIkITdv1nel5esXO3FqnCLccm9SWE6ZlKYpIaeLUKNb8EcmH
rRbL/P8/8HjK0cnjFZkbnaVlM7lvX3XxthppObjeBfpJMhrERZXzc8Upf2BYHwfd
xWMie2UKl3ACTXwFsRnPa8x1YNp4/yuYXdGYUBCQ9HA/ezVsT89ZsSCg9/OOr/yC
EfxbAybk0bR+LWjra7pCjbwFAgMBAAGjYjBgMB8GA1UdIwQYMBaAFKkEm1HEvDRZ
lChy2F2OeUc2mLf/MAkGA1UdEwQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwHQYD
VR0OBBYEFNiRb8eYOuF0RYU94gphl4E/ugvIMA0GCSqGSIb3DQEBCwUAA4IBAQBU
+EGzgbO2CNmmukfxQBqUBnAPPyC2btTrVYdaRwnOhMNGcmPXK2Y1xjz42I/igFKY
o15yKaPIu3mvL4Fa2DoV7RH26PKXSyivXz6kkJI8PerOaDTMNk8uQ5zL1+ziuKYp
3jpM+kHsse5zrO15D9RpUA7ueTAXlvnorA1BTD2tOspFspZIHk4k5CLAd+CW6jzj
i7lOmz+KC7hokFae9f3Pn7vLB2PsbtbVdjARcFSorfWnMUXiFgCIoDMuRdVQ/qrq
CAuFbMXGW4jgIhysC42V8qPpi/7FnaUPvRuw4ITgNCt6HYn18kmubhxHKOsR9cDv
Rvfyb302QucnMtY0FTVS
-----END CERTIFICATE-----
16 changes: 16 additions & 0 deletions vertx-proton/src/test/resources/broker-updated.csr
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
-----BEGIN NEW CERTIFICATE REQUEST-----
MIICmjCCAYICAQAwJTESMBAGA1UEAxMJbG9jYWxob3N0MQ8wDQYDVQQKEwZTZXJ2
ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCq3lbL8oRXdirtReK/
j48pXhj4nrTMCDncZfS21RymC6h6AHrWryMxJGdbef+Jl5TIz9+FQ5fHXMCrYAjB
fAzXOOPNIdHULWFbtFYvMBYt465rce6a9Cx+oLMzXF/dwEyp0UUw1qfohIHQXIkI
Tdv1nel5esXO3FqnCLccm9SWE6ZlKYpIaeLUKNb8EcmHrRbL/P8/8HjK0cnjFZkb
naVlM7lvX3XxthppObjeBfpJMhrERZXzc8Upf2BYHwfdxWMie2UKl3ACTXwFsRnP
a8x1YNp4/yuYXdGYUBCQ9HA/ezVsT89ZsSCg9/OOr/yCEfxbAybk0bR+LWjra7pC
jbwFAgMBAAGgMDAuBgkqhkiG9w0BCQ4xITAfMB0GA1UdDgQWBBTYkW/HmDrhdEWF
PeIKYZeBP7oLyDANBgkqhkiG9w0BAQsFAAOCAQEAVPEi+keitw80PwR16sr5EhGk
79OxNemiYaqmUIGBu3mdIDn1FGxoUCrSfwpOZ6f5Y8uF4Or8E8q+Gn6yY0LYOu7e
gYK2OFw1VODdN+07vjqZfdIsRLU1vyZoeIud70FR7Tn/1FatvpHP4/U2fM/NiiW/
tN+xPS3mShvlICPhSKFi7qelFlA2sxLO7RHJert8FXDSWzqIQi2fEeR1bVGcueR0
pzN8nxvr7VW9/4YT/A2qPNip+UAJ6SWTsTZunAokSGVqyg9GHyoPgW+8o5zy2OKW
bkgVQU7EtRzG620o7ojRKmIzW/jqPxKqtbH7VdUo5nAGzDO5Ay5EOqv/QKl5Rw==
-----END NEW CERTIFICATE REQUEST-----
Binary file not shown.
19 changes: 19 additions & 0 deletions vertx-proton/src/test/resources/ca-updated.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDGjCCAgKgAwIBAgIEFgwG/zANBgkqhkiG9w0BAQsFADA0MRgwFgYDVQQDEw9t
eS12ZXJ0eC1jYS5vcmcxGDAWBgNVBAoTD015IFRydXN0ZWQgSW5jLjAgFw0yNDEx
MTUxNjI0MjBaGA8yMDUyMDQwMTE2MjQyMFowNDEYMBYGA1UEAxMPbXktdmVydHgt
Y2Eub3JnMRgwFgYDVQQKEw9NeSBUcnVzdGVkIEluYy4wggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQCW/eioYked6xetaXxv6I6RUqx+p3hTkGDUcAsdjbgZ
pfHlBsUdzvDXXBjzwBDSHTE34Qqmu/WI3TAZYswBzDTBOCETAYrqZw04NuLuT7Cj
ZyuECANPIGlWQBRvG2AEIEj1nVHk8KyE0qoUVEw/MOoFCdRa/LsbIaljrPG66s1s
zN+WjSh+489lGR7UadxghBInkyjBUkeQ2psDmbyr8VR32y3EJASbPVpwh8YDDTh5
HZS0JC0G78DtD0zL2xdHhyBoLy9zI13LPNyNDKYoHrRgfg4s+T8wAgNB01YHzZAm
JHoTArRe0816koYE1ND7JWBuvjvFnhdDIglNUblOFtxFAgMBAAGjMjAwMA8GA1Ud
EwEB/wQFMAMBAf8wHQYDVR0OBBYEFKkEm1HEvDRZlChy2F2OeUc2mLf/MA0GCSqG
SIb3DQEBCwUAA4IBAQBOgLOI7bYM5r5HAXwk6fWO+DJomhk2NeRwU9vCLa9s1ahV
rrIH0aZiQuCntPRWwZqTCtLR/nwZXCCwG+GR91sZnl6IfcJ0EzP6OSYTx1PQSNbW
Lc0jNmGWgKqpnhmzsF64ZKxETNTSD0mdXPS5LHWcGrvqoUAwn8tS5UTPZfUS32kj
FSU6R7INYMQ2gZshGfyWj46lLsDMiL+5WbSTrp0m86qBZ+PcMxZhXE3FCxYM8sWe
S/DdeILzaMLeVpJzseJWSL26pK7quoBCZhblP17CueTeoCatq/4J4tzoVSi0DudL
5BwpbW5xjHT+afySFRDxZsM6YpOpATNof1pjUSMH
-----END CERTIFICATE-----
Binary file not shown.