Currently I am editing existing implementation of SAML support on my project using Spring Security. I have multiple IdentityProviders, for which I store data in the database. Using my app UI I can add new IdentityProviders on runtime, which will be added to CachingMetadataManager. After that, refreshMetadata is called. However I have JKSKeyManager, which is loaded on app startup and loads a single JKS keystore which is used for all metadatas for all IdentityProviders. I want a user to be able to upload (or paste) a private key using my app UI during IdentityProvider creation on runtime, so that, different key can be used for different IdentityProvider, but I don't know how. There's no difference for me if I store the keys in JKS file or somewhere else. There's no spring boot and I am afraid there is no ability to upgrade the library versions/migrate to other libraries.
The key manager injection looks like this:
@Bean
public KeyManager keyManager() {
DefaultResourceLoader loader = new DefaultResourceLoader();
Resource storeFile = loader
.getResource(environment.getProperty("server.ssl.key-store"));
Map<String, String> passwords = new HashMap<>();
passwords.put(environment.getProperty("server.ssl.key-alias"), environment.getProperty("server.ssl.key-store-password"));
String defaultKey = "spring";
return new JKSKeyManager(storeFile, environment.getProperty("server.ssl.key-store-password"), passwords, defaultKey);
}
SAML extension used is spring-security-saml2-core (1.0.3.RELEASE) from org.springframework.security.extensions. And Spring Security vesion is 3.2.9.RELEASE.
Since JKSKeyManager
computes all the available keys at construction time, a custom implementation of KeyManager
would likely be best.
Something like the following, for example:
public DynamicJKSKeyManager extends JKSKeyManager {
private final KeyStore keyStore;
public KeyStoreKeyManager(KeyStore keyStore, Map<String, String> passwords, String defaultKey) {
super(keyStore, passwords, defaultKey);
this.keyStore = keyStore;
}
@Override
public Set<String> getAvailableCredentials() {
try {
Set<String> availableKeys = new HashSet<String>();
Enumeration<String> aliases = keyStore.aliases();
while (aliases.hasMoreElements()) {
availableKeys.add(aliases.nextElement());
}
return availableKeys;
} catch (KeyStoreException e) {
throw new RuntimeException("Unable to load aliases from keyStore", e);
}
}
}
would change the getAvailableCredentials
method to read the KeyStore
aliases on each invocation.
Then, when you need to add a key to the KeyStore
, you can use the KeyStore
API to do it.
Of course, as you mentioned, you don't have to use KeyStore
. Wherever you store your keys, you can implement your own KeyManager
and use OpenSAML's CollectionCredentialResolver
instead of KeyStoreCredentialResolver
.