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

TLS: Introduce *-alias to select specific key to use (or cert to trust) #17884

Merged
merged 1 commit into from
Jun 16, 2021
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,14 @@ public class CertificateConfig {
public String keyStorePassword;

/**
* An optional trust store which holds the certificate information of the certificates to trust
* An optional parameter to select a specific key in the key store. When SNI is disabled, if the key store contains multiple
* keys and no alias is specified, the behavior is undefined.
*/
@ConfigItem
public Optional<String> keyStoreKeyAlias;

/**
* An optional trust store which holds the certificate information of the certificates to trust.
*/
@ConfigItem
public Optional<Path> trustStoreFile;
Expand All @@ -99,4 +106,11 @@ public class CertificateConfig {
*/
@ConfigItem
public Optional<String> trustStorePassword;

/**
* An optional parameter to trust only one specific certificate in the trust store (instead of trusting all certificates in
* the store).
*/
@ConfigItem
public Optional<String> trustStoreCertAlias;
}
Original file line number Diff line number Diff line change
Expand Up @@ -615,22 +615,12 @@ private static HttpServerOptions createSslOptions(HttpBuildTimeConfig buildTimeC
if (!certificates.isEmpty() && !keys.isEmpty()) {
createPemKeyCertOptions(certificates, keys, serverOptions);
} else if (keyStoreFile.isPresent()) {
final Path keyStorePath = keyStoreFile.get();
final Optional<String> keyStoreFileType = sslConfig.certificate.keyStoreFileType;
final String type;
if (keyStoreFileType.isPresent()) {
type = keyStoreFileType.get().toLowerCase();
} else {
type = findKeystoreFileType(keyStorePath);
}

byte[] data = getFileContent(keyStorePath);
final Optional<String> keyStoreProvider = sslConfig.certificate.keyStoreProvider;
KeyStoreOptions options = new KeyStoreOptions()
.setPassword(keystorePassword)
.setValue(Buffer.buffer(data))
.setType(type.toUpperCase())
.setProvider(keyStoreProvider.orElse(null));
KeyStoreOptions options = createKeyStoreOptions(
keyStoreFile.get(),
keystorePassword,
sslConfig.certificate.keyStoreFileType,
sslConfig.certificate.keyStoreProvider,
sslConfig.certificate.keyStoreKeyAlias);
serverOptions.setKeyCertOptions(options);
} else {
return null;
Expand All @@ -640,16 +630,13 @@ private static HttpServerOptions createSslOptions(HttpBuildTimeConfig buildTimeC
if (!trustStorePassword.isPresent()) {
throw new IllegalArgumentException("No trust store password provided");
}
final String type;
final Optional<String> trustStoreFileType = sslConfig.certificate.trustStoreFileType;
final Path trustStoreFilePath = trustStoreFile.get();
if (trustStoreFileType.isPresent()) {
type = trustStoreFileType.get().toLowerCase();
} else {
type = findKeystoreFileType(trustStoreFilePath);
}
createTrustStoreOptions(trustStoreFilePath, trustStorePassword.get(), type,
sslConfig.certificate.trustStoreProvider.orElse(null), serverOptions);
KeyStoreOptions options = createKeyStoreOptions(
trustStoreFile.get(),
trustStorePassword.get(),
sslConfig.certificate.trustStoreFileType,
sslConfig.certificate.trustStoreProvider,
sslConfig.certificate.trustStoreCertAlias);
serverOptions.setTrustOptions(options);
}

for (String cipher : sslConfig.cipherSuites.orElse(Collections.emptyList())) {
Expand All @@ -675,6 +662,25 @@ private static HttpServerOptions createSslOptions(HttpBuildTimeConfig buildTimeC
return serverOptions;
}

private static KeyStoreOptions createKeyStoreOptions(Path keyStorePath, String password, Optional<String> keyStoreFileType,
Optional<String> keyStoreProvider, Optional<String> keyStoreAlias) throws IOException {
final String type;
if (keyStoreFileType.isPresent()) {
type = keyStoreFileType.get().toLowerCase();
} else {
type = findKeystoreFileType(keyStorePath);
}

byte[] data = getFileContent(keyStorePath);
KeyStoreOptions options = new KeyStoreOptions()
.setPassword(password)
.setValue(Buffer.buffer(data))
.setType(type.toUpperCase())
.setProvider(keyStoreProvider.orElse(null))
.setAlias(keyStoreAlias.orElse(null));
return options;
}

private static byte[] getFileContent(Path path) throws IOException {
byte[] data;
final InputStream resource = Thread.currentThread().getContextClassLoader().getResourceAsStream(path.toString());
Expand Down Expand Up @@ -717,17 +723,6 @@ private static void createPemKeyCertOptions(List<Path> certFile, List<Path> keyF
serverOptions.setPemKeyCertOptions(pemKeyCertOptions);
}

private static void createTrustStoreOptions(Path trustStoreFile, String trustStorePassword,
String trustStoreFileType, String trustStoreProvider, HttpServerOptions serverOptions) throws IOException {
byte[] data = getFileContent(trustStoreFile);
KeyStoreOptions options = new KeyStoreOptions()
.setPassword(trustStorePassword)
.setValue(Buffer.buffer(data))
.setType(trustStoreFileType.toUpperCase())
.setProvider(trustStoreProvider);
serverOptions.setTrustOptions(options);
}

private static String findKeystoreFileType(Path storePath) {
final String pathName = storePath.toString();
if (pathName.endsWith(".p12") || pathName.endsWith(".pkcs12") || pathName.endsWith(".pfx")) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
vertx.event-loops.size=2
quarkus.http.ssl.certificate.key-store-file=server-keystore.jks
quarkus.http.ssl.certificate.key-store-password=password
quarkus.http.ssl.certificate.key-store-key-alias=server
quarkus.http.ssl.certificate.trust-store-file=server-truststore.jks
quarkus.http.ssl.certificate.trust-store-password=password
quarkus.http.ssl.certificate.trust-store-cert-alias=mykey-1
quarkus.http.ssl.client-auth=REQUIRED
quarkus.http.access-log.enabled=true
quarkus.http.access-log.log-to-file=true
Expand Down
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;

import org.apache.commons.lang3.exception.ExceptionUtils;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -38,6 +39,21 @@ public class Http2TestCase {

@Test
public void testHttp2EnabledSsl() throws ExecutionException, InterruptedException {
runHttp2EnabledSsl("client-keystore-1.jks");
}

@Test
public void testHttp2EnabledSslWithNotSelectedClientCert() throws ExecutionException, InterruptedException {
// client-keystore-2.jks contains the key pair matching mykey-2 in server-truststore.jks,
// but only mykey-1 is "selected" via its alias in application.properties
ExecutionException exc = Assertions.assertThrows(ExecutionException.class,
() -> runHttp2EnabledSsl("client-keystore-2.jks"));

Assertions.assertEquals("SSLHandshakeException: Received fatal alert: bad_certificate",
ExceptionUtils.getRootCauseMessage(exc));
}

private void runHttp2EnabledSsl(String keystoreName) throws InterruptedException, ExecutionException {
Assumptions.assumeTrue(JdkSSLEngineOptions.isAlpnAvailable()); //don't run on JDK8
Vertx vertx = Vertx.vertx();
try {
Expand All @@ -46,7 +62,7 @@ public void testHttp2EnabledSsl() throws ExecutionException, InterruptedExceptio
.setProtocolVersion(HttpVersion.HTTP_2)
.setSsl(true)
.setKeyStoreOptions(
new JksOptions().setPath("src/test/resources/client-keystore.jks").setPassword("password"))
new JksOptions().setPath("src/test/resources/" + keystoreName).setPassword("password"))
.setTrustStoreOptions(
new JksOptions().setPath("src/test/resources/client-truststore.jks").setPassword("password"));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public void testRouteRegistrationMTLS() {
RequestSpecification spec = new RequestSpecBuilder()
.setBaseUri(String.format("%s://%s", url.getProtocol(), url.getHost()))
.setPort(url.getPort())
.setKeyStore("client-keystore.jks", "password")
.setKeyStore("client-keystore-1.jks", "password")
.setTrustStore("client-truststore.jks", "password")
.build();
given().spec(spec).get("/my-path").then().body(containsString("OK"));
Expand Down
Binary file not shown.