Skip to content

Commit

Permalink
Add aliasPassword property to keystore options
Browse files Browse the repository at this point in the history
Fixes eclipse-vertx#3970

In eclipse-vertx#3933 we added an alias property to keystore options that allows to choose a keystore entry when there are several instead of letting the JVM selecting the first one.

This is a follow-up change that allows to specify a password for the alias when it is different than the store's password.

Signed-off-by: Thomas Segismont <[email protected]>
  • Loading branch information
tsegismont committed Jun 22, 2021
1 parent 8c3e502 commit 4381e2f
Show file tree
Hide file tree
Showing 11 changed files with 70 additions and 29 deletions.
8 changes: 4 additions & 4 deletions src/main/java/io/vertx/core/net/JksOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,13 @@ public JksOptions setValue(Buffer value) {
}

@Override
public String getAlias() {
return super.getAlias();
public JksOptions setAlias(String alias) {
return (JksOptions) super.setAlias(alias);
}

@Override
public JksOptions setAlias(String alias) {
return (JksOptions) super.setAlias(alias);
public JksOptions setAliasPassword(String aliasPassword) {
return (JksOptions) super.setAliasPassword(aliasPassword);
}

@Override
Expand Down
10 changes: 5 additions & 5 deletions src/main/java/io/vertx/core/net/KeyStoreOptions.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2011-2019 Contributors to the Eclipse Foundation
* Copyright (c) 2011-2021 Contributors to the Eclipse Foundation
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
Expand Down Expand Up @@ -127,13 +127,13 @@ public KeyStoreOptions setValue(Buffer value) {
}

@Override
public String getAlias() {
return super.getAlias();
public KeyStoreOptions setAlias(String alias) {
return (KeyStoreOptions) super.setAlias(alias);
}

@Override
public KeyStoreOptions setAlias(String alias) {
return (KeyStoreOptions) super.setAlias(alias);
public KeyStoreOptions setAliasPassword(String aliasPassword) {
return (KeyStoreOptions) super.setAliasPassword(aliasPassword);
}

@Override
Expand Down
21 changes: 20 additions & 1 deletion src/main/java/io/vertx/core/net/KeyStoreOptionsBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public abstract class KeyStoreOptionsBase implements KeyCertOptions, TrustOption
private String path;
private Buffer value;
private String alias;
private String aliasPassword;

/**
* Default constructor
Expand All @@ -59,6 +60,7 @@ protected KeyStoreOptionsBase(KeyStoreOptionsBase other) {
this.path = other.path;
this.value = other.value;
this.alias = other.alias;
this.alias = other.aliasPassword;
}

protected String getType() {
Expand Down Expand Up @@ -154,6 +156,23 @@ public KeyStoreOptionsBase setAlias(String alias) {
return this;
}

/**
* @return the alias password for a server certificate when the keystore has more than one, or {@code null}
*/
public String getAliasPassword() {
return aliasPassword;
}

/**
* Set the alias password for a server certificate when the keystore has more than one.
*
* @return a reference to this, so the API can be used fluently
*/
public KeyStoreOptionsBase setAliasPassword(String aliasPassword) {
this.aliasPassword = aliasPassword;
return this;
}

KeyStoreHelper getHelper(Vertx vertx) throws Exception {
if (helper == null) {
Supplier<Buffer> value;
Expand All @@ -164,7 +183,7 @@ KeyStoreHelper getHelper(Vertx vertx) throws Exception {
} else {
return null;
}
helper = new KeyStoreHelper(KeyStoreHelper.loadKeyStore(type, provider, password, value, getAlias()), password);
helper = new KeyStoreHelper(KeyStoreHelper.loadKeyStore(type, provider, password, value, getAlias()), password, getAliasPassword());
}
return helper;
}
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/io/vertx/core/net/PemKeyCertOptions.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2011-2019 Contributors to the Eclipse Foundation
* Copyright (c) 2011-2021 Contributors to the Eclipse Foundation
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
Expand Down Expand Up @@ -402,7 +402,7 @@ KeyStoreHelper getHelper(Vertx vertx) throws Exception {
certs.add(vertx.fileSystem().readFileBlocking(((VertxInternal)vertx).resolveFile(certPath).getAbsolutePath()));
}
certs.addAll(certValues);
helper = new KeyStoreHelper(KeyStoreHelper.loadKeyCert(keys, certs), KeyStoreHelper.DUMMY_PASSWORD);
helper = new KeyStoreHelper(KeyStoreHelper.loadKeyCert(keys, certs), KeyStoreHelper.DUMMY_PASSWORD, null);
}
return helper;
}
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/io/vertx/core/net/PemTrustOptions.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2011-2019 Contributors to the Eclipse Foundation
* Copyright (c) 2011-2021 Contributors to the Eclipse Foundation
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
Expand Down Expand Up @@ -187,7 +187,7 @@ KeyStoreHelper getHelper(Vertx vertx) throws Exception {
map(path -> ((VertxInternal)vertx).resolveFile(path).getAbsolutePath()).
map(vertx.fileSystem()::readFileBlocking);
certValues = Stream.concat(certValues, this.certValues.stream());
helper = new KeyStoreHelper(KeyStoreHelper.loadCA(certValues), null);
helper = new KeyStoreHelper(KeyStoreHelper.loadCA(certValues), null, null);
}
return helper;
}
Expand Down
10 changes: 5 additions & 5 deletions src/main/java/io/vertx/core/net/PfxOptions.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2011-2019 Contributors to the Eclipse Foundation
* Copyright (c) 2011-2021 Contributors to the Eclipse Foundation
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
Expand Down Expand Up @@ -70,13 +70,13 @@ public PfxOptions setValue(Buffer value) {
}

@Override
public String getAlias() {
return super.getAlias();
public PfxOptions setAlias(String alias) {
return (PfxOptions) super.setAlias(alias);
}

@Override
public KeyStoreOptions setAlias(String alias) {
return (KeyStoreOptions) super.setAlias(alias);
public PfxOptions setAliasPassword(String aliasPassword) {
return (PfxOptions) super.setAliasPassword(aliasPassword);
}

@Override
Expand Down
24 changes: 14 additions & 10 deletions src/main/java/io/vertx/core/net/impl/KeyStoreHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
Expand All @@ -53,16 +52,17 @@ public class KeyStoreHelper {

private final String password;
private final KeyStore store;
private final String aliasPassword;
private final Map<String, X509KeyManager> wildcardMgrMap = new HashMap<>();
private final Map<String, X509KeyManager> mgrMap = new HashMap<>();
private final Map<String, TrustManagerFactory> trustMgrMap = new HashMap<>();

public KeyStoreHelper(KeyStore ks, String password) throws Exception {
public KeyStoreHelper(KeyStore ks, String password, String aliasPassword) throws Exception {
Enumeration<String> en = ks.aliases();
while (en.hasMoreElements()) {
String alias = en.nextElement();
Certificate cert = ks.getCertificate(alias);
if (ks.isCertificateEntry(alias) && ! alias.startsWith(DUMMY_CERT_ALIAS)){
if (ks.isCertificateEntry(alias) && !alias.startsWith(DUMMY_CERT_ALIAS)) {
final KeyStore keyStore = createEmptyKeyStore();
keyStore.setCertificateEntry("cert-1", cert);
TrustManagerFactory fact = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
Expand All @@ -84,16 +84,13 @@ public KeyStoreHelper(KeyStore ks, String password) throws Exception {
String dn = x509Cert.getSubjectX500Principal().getName();
domains.addAll(getX509CertificateCommonNames(dn));
if (!domains.isEmpty()) {
PrivateKey key = (PrivateKey) ks.getKey(alias, password != null ? password.toCharArray() : null);
char[] keyPassword = keyPassword(aliasPassword, password);
PrivateKey key = (PrivateKey) ks.getKey(alias, keyPassword);
Certificate[] tmp = ks.getCertificateChain(alias);
if (tmp == null) {
// It's a private key
continue;
}
List<X509Certificate> chain = Arrays.asList(tmp)
.stream()
.map(c -> (X509Certificate)c)
.collect(Collectors.toList());
X509KeyManager mgr = new X509KeyManager() {
@Override
public String[] getClientAliases(String s, Principal[] principals) {
Expand All @@ -113,7 +110,7 @@ public String chooseServerAlias(String s, Principal[] principals, Socket socket)
}
@Override
public X509Certificate[] getCertificateChain(String s) {
return chain.toArray(new X509Certificate[0]);
return Arrays.stream(tmp).map(X509Certificate.class::cast).toArray(X509Certificate[]::new);
}
@Override
public PrivateKey getPrivateKey(String s) {
Expand All @@ -132,14 +129,21 @@ public PrivateKey getPrivateKey(String s) {
}
this.store = ks;
this.password = password;
this.aliasPassword = aliasPassword;
}

public KeyManagerFactory getKeyMgrFactory() throws Exception {
KeyManagerFactory fact = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
fact.init(store, password != null ? password.toCharArray(): null);
char[] keyPassword = keyPassword(aliasPassword, password);
fact.init(store, keyPassword);
return fact;
}

private char[] keyPassword(String aliasPassword, String password) {
if (aliasPassword != null) return aliasPassword.toCharArray();
return (password != null) ? password.toCharArray() : null;
}

public X509KeyManager getKeyMgr(String serverName) {
X509KeyManager mgr = mgrMap.get(serverName);
if (mgr == null && !wildcardMgrMap.isEmpty()) {
Expand Down
10 changes: 10 additions & 0 deletions src/test/java/io/vertx/core/net/NetTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -1504,6 +1504,16 @@ public void testServerCertificateMultipleWrongAlias() throws Exception {
await();
}

@Test
public void testServerCertificateMultipleWithKeyPassword() throws Exception {
TLSTest test = new TLSTest()
.serverCert(Cert.MULTIPLE_JKS_ALIAS_PASSWORD)
.clientTrustAll(true);
test.run(true);
await();
assertEquals("fonky", cnOf(test.clientPeerCert()));
}

void testTLS(Cert<?> clientCert, Trust<?> clientTrust,
Cert<?> serverCert, Trust<?> serverTrust,
boolean requireClientAuth, boolean clientTrustAll,
Expand Down
1 change: 1 addition & 0 deletions src/test/java/io/vertx/test/tls/Cert.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,6 @@ public interface Cert<K extends KeyCertOptions> extends Supplier<K> {
.addKeyPath("tls/host5-key.pem").addCertPath("tls/host5-cert.pem");
Cert<JksOptions> MULTIPLE_JKS = () -> new JksOptions().setPath("tls/multiple.jks").setPassword("wibble").setAlias("precious");
Cert<JksOptions> MULTIPLE_JKS_WRONG_ALIAS = () -> new JksOptions().setPath("tls/multiple.jks").setPassword("wibble").setAlias("preciouss");
Cert<JksOptions> MULTIPLE_JKS_ALIAS_PASSWORD = () -> new JksOptions().setPath("tls/multiple-alias-password.jks").setPassword("wibble").setAlias("fonky").setAliasPassword("family");

}
Binary file not shown.
7 changes: 7 additions & 0 deletions src/test/resources/tls/ssl.txt
Original file line number Diff line number Diff line change
Expand Up @@ -345,3 +345,10 @@ keytool -v -genkeypair -alias other -keyalg RSA -keysize 4096 -keystore multiple

The generated file is used to setup a server and check we can force Vert.x to use a specific alias.
By default the SSL engine selects the first entry in the keystore (which corresponds to the second generated key pair - "other").

## JKS Store with multiple entries and key passwords

Same as previous one but with key passwords.

keytool -v -genkeypair -alias fonky -dname "CN=fonky, OU=Vert.x, O=Eclipse" -keyalg RSA -keysize 4096 -keystore multiple-alias-password.jks -validity 36500 -storepass wibble -keypass family -storetype jks
keytool -v -genkeypair -alias reflection -dname "CN=reflection, OU=Vert.x, O=Eclipse" -keyalg RSA -keysize 4096 -keystore multiple-alias-password.jks -validity 36500 -storepass wibble -keypass eternal -storetype jks

0 comments on commit 4381e2f

Please sign in to comment.