Skip to content

Add support for adding additional identity material at runtime #655

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

24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,30 @@ KeyManagerUtils.addIdentityRoute(keyManager, "client-alias-one", "https://localh
// Override existing routes
KeyManagerUtils.overrideIdentityRoute(keyManager, "client-alias-two", "https://localhost:9463/", "https://localhost:9473/")
```
##### Managing additional identities at runtime
Please beware when using multiple identities from a keystore. The alias name from the key entry within the keystore should be unique across all the identities.
```text
SSLFactory sslFactory = SSLFactory.builder()
.withInflatableIdentityMaterial()
.withIdentityMaterial(Paths.get("/path/to/your/identity-1.jks"), "password".toCharArray())
.withTrustMaterial("truststore.jks", password)
.withIdentityRoute("client-alias-one", "https://localhost:8443/", "https://localhost:8453/")
.build();

X509ExtendedKeyManager keyManager = sslFactory.getKeyManager().get();

// Adding identity and an additional route
KeyStore identityTwo = KeyStoreUtils.loadKeyStore(Paths.get("/path/to/your/identity-2.jks"), "password".toCharArray());
KeyManagerUtils.addIdentityMaterial(keyManager, "client-alias-two", identityTwo, "password".toCharArray());
KeyManagerUtils.addIdentityRoute(keyManager, "client-alias-two", "https://localhost:8463/", "https://localhost:8473/");

// Getting existing list of aliases from the inflatable key manager
List<String> aliases = KeyManagerUtils.getAliases(keyManager);

// Removing identity
KeyManagerUtils.removeIdentityMaterial(keyManager, "client-alias-two");
KeyManagerUtils.removeIdentityRoute(keyManager, "client-alias-two");
```
##### Managing ssl session
```text
SSLFactory sslFactory = SSLFactory.builder()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ public static class Builder {
private boolean swappableSslParametersEnabled = false;
private boolean loggingKeyManagerEnabled = false;
private boolean loggingTrustManagerEnabled = false;
private boolean inflatableKeyManagerEnabled = false;

private int sessionTimeoutInSeconds = -1;
private int sessionCacheSizeInBytes = -1;
Expand Down Expand Up @@ -708,6 +709,11 @@ public Builder withLoggingIdentityMaterial() {
return this;
}

public Builder withInflatableIdentityMaterial() {
inflatableKeyManagerEnabled = true;
return this;
}

public Builder withInflatableTrustMaterial() {
trustManagers.add(TrustManagerUtils.createInflatableTrustManager());
return this;
Expand Down Expand Up @@ -949,6 +955,7 @@ private X509ExtendedKeyManager createKeyManager() {
.withIdentities(identities)
.withSwappableKeyManager(swappableKeyManagerEnabled)
.withLoggingKeyManager(loggingKeyManagerEnabled)
.withInflatableKeyManager(inflatableKeyManagerEnabled)
.withIdentityRoute(preferredAliasToHost)
.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,11 @@
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
* Represents an ordered list of {@link X509ExtendedKeyManager} with most-preferred managers first.
Expand Down Expand Up @@ -64,15 +65,15 @@
*/
public final class AggregatedX509ExtendedKeyManager extends X509ExtendedKeyManager implements CombinableX509KeyManager, RoutableX509KeyManager {

private final List<X509ExtendedKeyManager> keyManagers;
final Map<String, X509ExtendedKeyManager> keyManagers;
private final Map<String, List<URI>> preferredAliasToHost;

/**
* Creates a new {@link AggregatedX509ExtendedKeyManager}.
*
* @param keyManagers the {@link X509ExtendedKeyManager}, ordered with the most-preferred managers first.
*/
public AggregatedX509ExtendedKeyManager(List<? extends X509ExtendedKeyManager> keyManagers) {
public AggregatedX509ExtendedKeyManager(Map<String, ? extends X509ExtendedKeyManager> keyManagers) {
this(keyManagers, Collections.emptyMap());
}

Expand All @@ -82,10 +83,10 @@ public AggregatedX509ExtendedKeyManager(List<? extends X509ExtendedKeyManager> k
* @param keyManagers the {@link X509ExtendedKeyManager}, ordered with the most-preferred managers first.
* @param preferredAliasToHost the preferred client alias to be used for the given host
*/
public AggregatedX509ExtendedKeyManager(List<? extends X509ExtendedKeyManager> keyManagers,
public AggregatedX509ExtendedKeyManager(Map<String, ? extends X509ExtendedKeyManager> keyManagers,
Map<String, List<URI>> preferredAliasToHost) {
this.keyManagers = Collections.unmodifiableList(keyManagers);
this.preferredAliasToHost = new HashMap<>(preferredAliasToHost);
this.keyManagers = Collections.synchronizedMap(new LinkedHashMap<>(keyManagers));
this.preferredAliasToHost = new ConcurrentHashMap<>(preferredAliasToHost);
}

/**
Expand Down Expand Up @@ -187,8 +188,8 @@ public String[] getServerAliases(String keyType, Principal[] issuers) {
}

@Override
public List<X509ExtendedKeyManager> getInnerKeyManagers() {
return keyManagers;
public Map<String, X509ExtendedKeyManager> getInnerKeyManagers() {
return Collections.unmodifiableMap(keyManagers);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import javax.net.ssl.X509KeyManager;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.Predicate;
Expand All @@ -30,20 +31,20 @@
*
* @author Hakan Altindag
*/
interface CombinableX509KeyManager extends X509KeyManager {
public interface CombinableX509KeyManager extends X509KeyManager {

List<X509ExtendedKeyManager> getInnerKeyManagers();
Map<String, X509ExtendedKeyManager> getInnerKeyManagers();

default <T> T extractInnerField(Function<X509ExtendedKeyManager, T> keyManagerMapper, Predicate<T> predicate) {
return getInnerKeyManagers().stream()
return getInnerKeyManagers().values().stream()
.map(keyManagerMapper)
.filter(predicate)
.findFirst()
.orElse(null);
}

default String[] getAliases(Function<X509ExtendedKeyManager, String[]> aliasExtractor) {
List<String> aliases = getInnerKeyManagers().stream()
List<String> aliases = getInnerKeyManagers().values().stream()
.map(aliasExtractor)
.filter(Objects::nonNull)
.flatMap(Arrays::stream)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
*
* @author Hakan Altindag
*/
class DelegatingX509ExtendedKeyManager extends DelegatingKeyManager<X509ExtendedKeyManager> {
public class DelegatingX509ExtendedKeyManager extends DelegatingKeyManager<X509ExtendedKeyManager> {

public DelegatingX509ExtendedKeyManager(X509ExtendedKeyManager keyManager) {
super(keyManager);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@
*
* @author Hakan Altindag
*/
public final class HotSwappableX509ExtendedKeyManager extends DelegatingX509ExtendedKeyManager {
public class HotSwappableX509ExtendedKeyManager extends DelegatingX509ExtendedKeyManager {

private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private final Lock readLock = readWriteLock.readLock();
private final Lock writeLock = readWriteLock.writeLock();
protected final Lock readLock = readWriteLock.readLock();
protected final Lock writeLock = readWriteLock.writeLock();

public HotSwappableX509ExtendedKeyManager(X509ExtendedKeyManager keyManager) {
super(keyManager);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Copyright 2019 Thunderberry.
*
* Licensed 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 nl.altindag.ssl.keymanager;

import nl.altindag.ssl.util.KeyManagerUtils;

import javax.net.ssl.X509ExtendedKeyManager;
import java.security.KeyStore;
import java.util.Collections;
import java.util.Map;

/**
* <strong>NOTE:</strong>
* Please don't use this class directly as it is part of the internal API. Class name and methods can be changed any time.
* Instead, use the {@link KeyManagerUtils KeyManagerUtils} which provides the same functionality
* while it has a stable API because it is part of the public API.
* <p>
* The Inflatable KeyManager has the capability to grow with newly added identity material at any moment in time.
* It can be added with {@link KeyManagerUtils#addIdentityMaterial(X509ExtendedKeyManager, String, KeyStore, char[])}
* or with {@link KeyManagerUtils#addIdentityMaterial(X509ExtendedKeyManager, String, X509ExtendedKeyManager)}
*
* @author Hakan Altindag
*/
public class InflatableX509ExtendedKeyManager extends HotSwappableX509ExtendedKeyManager {

public InflatableX509ExtendedKeyManager() {
this("dummy", KeyManagerUtils.createDummyKeyManager());
}

public InflatableX509ExtendedKeyManager(String alias, X509ExtendedKeyManager keyManager) {
super(keyManager instanceof AggregatedX509ExtendedKeyManager ? keyManager : new AggregatedX509ExtendedKeyManager(Collections.singletonMap(alias, keyManager)));
}

public void addIdentity(String alias, X509ExtendedKeyManager keyManager) {
writeLock.lock();

try {
AggregatedX509ExtendedKeyManager aggregatedKeyManager = (AggregatedX509ExtendedKeyManager) getInnerKeyManager();
aggregatedKeyManager.keyManagers.remove("dummy");
aggregatedKeyManager.keyManagers.put(alias, keyManager);
} finally {
writeLock.unlock();
}
}

public void removeIdentity(String alias) {
writeLock.lock();

try {
((AggregatedX509ExtendedKeyManager) getInnerKeyManager()).keyManagers.remove(alias);
} finally {
writeLock.unlock();
}
}

public Map<String, X509ExtendedKeyManager> getAliasToIdentity() {
readLock.lock();

try {
return Collections.unmodifiableMap(((AggregatedX509ExtendedKeyManager) getInnerKeyManager()).keyManagers);
} finally {
readLock.unlock();
}
}

}
Loading