Skip to content

Commit

Permalink
Add support for a Spring Boot custom key alias (line#1865)
Browse files Browse the repository at this point in the history
Motivation:

The `ssl.keyAlias` configuration in Spring Boot is currently
unsupported.

Modifications:

- Add `CustomAliasKeyManagerFactory` and `CustomAliasX509ExtendedKeyManager`
- Wrap the `KeyManagerFactory` with `CustomAliasKeyManagerFactory` to
  support custom key alias
- Miscellaneous:
  - Add `@Nullable` annotations where necessary

Result:

- Fixes line#1843
  • Loading branch information
trustin authored Jul 2, 2019
1 parent 294e987 commit 3315ad3
Show file tree
Hide file tree
Showing 13 changed files with 392 additions and 18 deletions.
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
*.bat text eol=crlf
*.br binary
*.gz binary
*.jks binary
*.pkcs12 binary

/gradlew text eol=lf

Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ public final class ArmeriaConfigurationUtil {
private static final Logger logger = LoggerFactory.getLogger(ArmeriaConfigurationUtil.class);

private static final HealthChecker[] EMPTY_HEALTH_CHECKERS = new HealthChecker[0];
private static final String[] EMPTY_PROTOCOL_NAMES = new String[0];

private static final String METER_TYPE = "server";

Expand Down Expand Up @@ -200,7 +201,8 @@ public static void configurePorts(ServerBuilder server, List<Port> ports) {
final String ip = p.getIp();
final String iface = p.getIface();
final int port = p.getPort();
final List<SessionProtocol> protocols = p.getProtocols();
final List<SessionProtocol> protocols = firstNonNull(p.getProtocols(),
ImmutableList.of(SessionProtocol.HTTP));

if (ip == null) {
if (iface == null) {
Expand Down Expand Up @@ -439,7 +441,7 @@ public static void configureTls(ServerBuilder sb, Ssl ssl,
}
final List<String> enabledProtocols = ssl.getEnabledProtocols();
if (enabledProtocols != null) {
sslContextBuilder.protocols(enabledProtocols.toArray(new String[enabledProtocols.size()]));
sslContextBuilder.protocols(enabledProtocols.toArray(EMPTY_PROTOCOL_NAMES));
}
final List<String> ciphers = ssl.getCiphers();
if (ciphers != null) {
Expand All @@ -465,8 +467,11 @@ private static KeyManagerFactory getKeyManagerFactory(
store = loadKeyStore(ssl.getKeyStoreType(), ssl.getKeyStore(), ssl.getKeyStorePassword());
}

final KeyManagerFactory keyManagerFactory =
KeyManagerFactory keyManagerFactory =
KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
if (ssl.getKeyAlias() != null) {
keyManagerFactory = new CustomAliasKeyManagerFactory(keyManagerFactory, ssl.getKeyAlias());
}

String keyPassword = ssl.getKeyPassword();
if (keyPassword == null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright 2019 LINE Corporation
*
* LINE Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package com.linecorp.armeria.internal.spring;

import java.security.InvalidAlgorithmParameterException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.KeyManagerFactorySpi;
import javax.net.ssl.ManagerFactoryParameters;
import javax.net.ssl.X509ExtendedKeyManager;

final class CustomAliasKeyManagerFactory extends KeyManagerFactory {
CustomAliasKeyManagerFactory(KeyManagerFactory delegate, String alias) {
super(new KeyManagerFactorySpi() {
@Override
protected void engineInit(KeyStore ks, char[] password)
throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException {
delegate.init(ks, password);
}

@Override
protected void engineInit(ManagerFactoryParameters spec) throws InvalidAlgorithmParameterException {
delegate.init(spec);
}

@Override
protected KeyManager[] engineGetKeyManagers() {
final KeyManager[] keyManagers = delegate.getKeyManagers().clone();
for (int i = 0; i < keyManagers.length; i++) {
if (keyManagers[i] instanceof X509ExtendedKeyManager) {
final X509ExtendedKeyManager keyManager = (X509ExtendedKeyManager) keyManagers[i];
keyManagers[i] = new CustomAliasX509ExtendedKeyManager(keyManager, alias);
}
}
return keyManagers;
}
}, delegate.getProvider(), delegate.getAlgorithm());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* Copyright 2019 LINE Corporation
*
* LINE Corporation licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package com.linecorp.armeria.internal.spring;

import java.net.Socket;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;

import javax.net.ssl.SSLEngine;
import javax.net.ssl.X509ExtendedKeyManager;

import com.google.common.base.MoreObjects;

/**
* {@link X509ExtendedKeyManager} chooses the given alias for the server side.
*/
final class CustomAliasX509ExtendedKeyManager extends X509ExtendedKeyManager {

private final X509ExtendedKeyManager delegate;

private final String alias;

CustomAliasX509ExtendedKeyManager(X509ExtendedKeyManager delegate, String alias) {
this.delegate = delegate;
this.alias = alias;
}

@Override
public String chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine engine) {
return alias;
}

@Override
public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
return alias;
}

@Override
public String[] getServerAliases(String keyType, Principal[] issuers) {
return delegate.getServerAliases(keyType, issuers);
}

@Override
public String chooseEngineClientAlias(String[] keyType, Principal[] issuers, SSLEngine engine) {
return delegate.chooseEngineClientAlias(keyType, issuers, engine);
}

@Override
public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {
return delegate.chooseClientAlias(keyType, issuers, socket);
}

@Override
public String[] getClientAliases(String keyType, Principal[] issuers) {
return delegate.getClientAliases(keyType, issuers);
}

@Override
public X509Certificate[] getCertificateChain(String alias) {
return delegate.getCertificateChain(alias);
}

@Override
public PrivateKey getPrivateKey(String alias) {
return delegate.getPrivateKey(alias);
}

@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("alias", alias)
.add("delegate", delegate)
.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,13 @@ public static class Port {
/**
* IP address to bind to. If not set, will bind to all addresses, e.g. {@code 0.0.0.0}.
*/
@Nullable
private String ip;

/**
* Network interface to bind to. If not set, will bind to the first detected network interface.
*/
@Nullable
private String iface;

/**
Expand All @@ -81,6 +83,7 @@ public static class Port {
/**
* Protocol that will be used in this ip/iface and port.
*/
@Nullable
private List<SessionProtocol> protocols;

/**
Expand Down
Loading

0 comments on commit 3315ad3

Please sign in to comment.