Skip to content

Commit

Permalink
Method for extracting the added alias (#11503)
Browse files Browse the repository at this point in the history
The approach used is very simple, the content of the alias is extracted and a new object representing a keystore is created only with that alias, this way it is possible to load only the desired alias and no more errors occur when we have multiple aliases in the keystore each with a password

Related issue #11430
  • Loading branch information
viniciusxyz authored Jan 27, 2025
1 parent 5fdb596 commit 93b612e
Show file tree
Hide file tree
Showing 4 changed files with 169 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@
import io.netty.handler.ssl.SslProvider;

import javax.net.ssl.KeyManagerFactory;
import java.security.Key;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.util.Optional;

/**
Expand Down Expand Up @@ -79,7 +81,34 @@ public static KeyManagerFactory storeToFactory(@NonNull SslConfiguration ssl, @N
if (keyPassword == null && pwd.isPresent()) {
keyPassword = pwd.get().toCharArray();
}
if (keyStore != null && ssl.getKey().getAlias().isPresent()) {
keyStore = extractKeystoreAlias(keyStore, ssl.getKey().getAlias().get(), keyPassword);
}
keyManagerFactory.init(keyStore, keyPassword);
return keyManagerFactory;
}

/**
* Creates a new {@link KeyStore} from the original containing only the selected alias.
*
* @param rootKeystore The original keystore
* @param alias The selected alias
* @param password Password of Alias
* @return {@link KeyStore} containing only the selected alias
*/
@NonNull
private static KeyStore extractKeystoreAlias(@NonNull KeyStore rootKeystore, @NonNull String alias, @Nullable char[] password) throws Exception {
if (!rootKeystore.containsAlias(alias)) {
throw new IllegalArgumentException("Alias " + alias + " not found in keystore");
}
Key key = rootKeystore.getKey(alias, password);
if (key == null) {
throw new IllegalStateException("There are no keys associated with the alias " + alias);
}
Certificate[] certChain = rootKeystore.getCertificateChain(alias);
KeyStore aliasKeystore = KeyStore.getInstance(rootKeystore.getType());
aliasKeystore.load(null, null);
aliasKeystore.setKeyEntry(alias, key, password, certChain);
return aliasKeystore;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package io.micronaut.http.netty

import io.micronaut.http.ssl.SslConfiguration
import spock.lang.Specification

import javax.net.ssl.KeyManagerFactory
import javax.net.ssl.X509KeyManager
import java.security.KeyStore
import java.security.cert.Certificate

class NettyTlsUtilsSpec extends Specification {
def "storeToFactory should return a KeyManagerFactory with only the selected alias"() {
given:

SslConfiguration sslConfig = Mock(SslConfiguration);
SslConfiguration.KeyConfiguration keyConfiguration = Mock(SslConfiguration.KeyConfiguration);
SslConfiguration.KeyStoreConfiguration keyStoreConfiguration = Mock(SslConfiguration.KeyStoreConfiguration);
sslConfig.isPreferOpenssl() >> false
sslConfig.getKey() >> keyConfiguration
sslConfig.getKeyStore() >> keyStoreConfiguration
keyConfiguration.getAlias() >> Optional.of("alias2")
keyConfiguration.getPassword() >> Optional.of("passwordAlias2")

String keystorePath = "src/test/resources/keystoreWithMultipleAlias.jks";
char[] keystorePassword = "password".toCharArray();

KeyStore rootKeyStore = KeyStore.getInstance("JKS");
try (FileInputStream fis = new FileInputStream(keystorePath)) {
rootKeyStore.load(fis, keystorePassword);
}

when:
KeyManagerFactory resultKeyStore = NettyTlsUtils.storeToFactory(sslConfig, rootKeyStore)

then:
resultKeyStore.getKeyManagers().size() == 1
def manager = (X509KeyManager) resultKeyStore.getKeyManagers().first()
def certificate = manager.getCertificateChain("alias2")
certificate[0].getSubjectX500Principal().toString() == "CN=localhost2, OU=Micronaut2, O=My Company, L=City, ST=State, C=BR";
}

def "storeToFactory should throw a exception if selected alias not exists"() {
given:

SslConfiguration sslConfig = Mock(SslConfiguration);
SslConfiguration.KeyConfiguration keyConfiguration = Mock(SslConfiguration.KeyConfiguration);
SslConfiguration.KeyStoreConfiguration keyStoreConfiguration = Mock(SslConfiguration.KeyStoreConfiguration);
sslConfig.isPreferOpenssl() >> false
sslConfig.getKey() >> keyConfiguration
sslConfig.getKeyStore() >> keyStoreConfiguration
keyConfiguration.getAlias() >> Optional.of("alias5")
keyConfiguration.getPassword() >> Optional.of("passwordAlias2")
KeyStore keystore = KeyStore.getInstance("JKS");
keystore.load(null, null)
keystore.containsAlias("any") >> false;

when:
NettyTlsUtils.storeToFactory(sslConfig, keystore)

then:
def e = thrown(IllegalArgumentException)
e.message == "Alias alias5 not found in keystore"
}

def "storeToFactory should throw a exception if key of alias is null"() {
given:

SslConfiguration sslConfig = Mock(SslConfiguration);
SslConfiguration.KeyConfiguration keyConfiguration = Mock(SslConfiguration.KeyConfiguration);
SslConfiguration.KeyStoreConfiguration keyStoreConfiguration = Mock(SslConfiguration.KeyStoreConfiguration);
sslConfig.isPreferOpenssl() >> false
sslConfig.getKey() >> keyConfiguration
sslConfig.getKeyStore() >> keyStoreConfiguration
keyConfiguration.getAlias() >> Optional.of("any")
keyConfiguration.getPassword() >> Optional.of("any")

KeyStore keystore = KeyStore.getInstance("JKS");
keystore.load(null, null)
keystore.containsAlias("any") >> false;
keystore.setCertificateEntry("any", Mock(Certificate))

when:
NettyTlsUtils.storeToFactory(sslConfig, keystore)

then:
def e = thrown(IllegalStateException)
e.message == "There are no keys associated with the alias any"
}

def "storeToFactory should not extract alias if Keystore is null"() {
given:

SslConfiguration sslConfig = Mock(SslConfiguration);
SslConfiguration.KeyConfiguration keyConfiguration = Mock(SslConfiguration.KeyConfiguration);
SslConfiguration.KeyStoreConfiguration keyStoreConfiguration = Mock(SslConfiguration.KeyStoreConfiguration);
sslConfig.isPreferOpenssl() >> false
sslConfig.getKey() >> keyConfiguration
sslConfig.getKeyStore() >> keyStoreConfiguration
keyConfiguration.getAlias() >> Optional.of("any")
keyConfiguration.getPassword() >> Optional.of("any")

when:
NettyTlsUtils.storeToFactory(sslConfig, null)

then:
0 * NettyTlsUtils.extractKeystoreAlias(_, _, _)
}

def "storeToFactory should not extract alias if alias is not defined"() {
given:

SslConfiguration sslConfig = Mock(SslConfiguration);
SslConfiguration.KeyConfiguration keyConfiguration = Mock(SslConfiguration.KeyConfiguration);
SslConfiguration.KeyStoreConfiguration keyStoreConfiguration = Mock(SslConfiguration.KeyStoreConfiguration);
sslConfig.isPreferOpenssl() >> false
sslConfig.getKey() >> keyConfiguration
sslConfig.getKeyStore() >> keyStoreConfiguration
keyConfiguration.getAlias() >> Optional.empty()
keyConfiguration.getPassword() >> Optional.empty()
keyStoreConfiguration.getPassword() >> Optional.of("any")

KeyStore keystore = KeyStore.getInstance("JKS");
keystore.load(null, null)

when:
NettyTlsUtils.storeToFactory(sslConfig, keystore)

then:
0 * NettyTlsUtils.extractKeystoreAlias(_, _, _)
}
}
9 changes: 9 additions & 0 deletions http-netty/src/test/resources/kesytoreGenerateCommands.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
## Commands used to generate keystore for testing

**keystoreWithMultipleAlias.jks**

```sh
keytool -genkeypair -v -keystore keystoreWithMultipleAlias.jks -storetype JKS -alias alias1 -keyalg RSA -keysize 2048 -validity 36500 -dname "CN=localhost1, OU=Micronaut1, O=My Company, L=City, ST=State, C=BR" -storepass password -keypass passwordAlias1
keytool -genkeypair -v -keystore keystoreWithMultipleAlias.jks -storetype JKS -alias alias2 -keyalg RSA -keysize 2048 -validity 36500 -dname "CN=localhost2, OU=Micronaut2, O=My Company, L=City, ST=State, C=BR" -storepass password -keypass passwordAlias2
keytool -genkeypair -v -keystore keystoreWithMultipleAlias.jks -storetype JKS -alias alias3 -keyalg RSA -keysize 2048 -validity 36500 -dname "CN=localhost3, OU=Micronaut3, O=My Company, L=City, ST=State, C=BR" -storepass password -keypass passwordAlias3
```
Binary file not shown.

0 comments on commit 93b612e

Please sign in to comment.