Skip to content

Commit

Permalink
Exposing the updateSSLOptions method of the NetServer class (#138)
Browse files Browse the repository at this point in the history
Exposing the updateSSLOptions method of the NetServer class

This fixes #137
  • Loading branch information
pcHwang2 authored Nov 29, 2024
1 parent 86c0ea0 commit b87b1e7
Show file tree
Hide file tree
Showing 10 changed files with 206 additions and 18 deletions.
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.

0 comments on commit b87b1e7

Please sign in to comment.