/*
* (C) Copyright 2014 Nuxeo SA (http://nuxeo.com/) and others.
*
* 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
*
* http://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.
*
* Contributors:
* Nelson Silva <nelson.silva@inevo.pt>
*/
package org.nuxeo.ecm.platform.auth.saml.key;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuxeo.runtime.model.ComponentInstance;
import org.nuxeo.runtime.model.DefaultComponent;
import org.opensaml.common.SAMLRuntimeException;
import org.opensaml.xml.security.CriteriaSet;
import org.opensaml.xml.security.SecurityException;
import org.opensaml.xml.security.credential.Credential;
import org.opensaml.xml.security.credential.KeyStoreCredentialResolver;
import org.opensaml.xml.security.criteria.EntityIDCriteria;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
/**
* An implementation of {@link KeyManager} that uses a JKS key store.
*/
public class KeyManagerImpl extends DefaultComponent implements KeyManager {
private static final Log log = LogFactory.getLog(KeyManagerImpl.class);
private static final String KEYSTORE_TYPE = "JKS";
KeyDescriptor config;
private KeyStore keyStore;
private KeyStoreCredentialResolver credentialResolver;
private Set<String> availableCredentials;
@Override
public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
config = (KeyDescriptor) contribution;
setup();
}
private void setup() {
if (config != null) {
try {
keyStore = getKeyStore(config.getKeystoreFilePath(), config.getKeystorePassword());
} catch (SecurityException e) {
throw new RuntimeException(e);
}
credentialResolver = new KeyStoreCredentialResolver(keyStore, config.getPasswords());
} else {
keyStore = null;
credentialResolver = null;
availableCredentials = null;
}
}
private KeyStore getKeyStore(String path, String password) throws SecurityException {
KeyStore ks;
try {
File rootKeystoreFile = new File(path);
if (!rootKeystoreFile.exists()) {
throw new SecurityException("Unable to find keyStore at " + new File(".").getAbsolutePath()
+ File.separator + path);
}
InputStream keystoreIS = new FileInputStream(rootKeystoreFile);
ks = java.security.KeyStore.getInstance(KEYSTORE_TYPE);
ks.load(keystoreIS, password.toCharArray());
} catch (KeyStoreException | IOException e) {
throw new SecurityException(e);
} catch (NoSuchAlgorithmException e) {
throw new SecurityException(e);
} catch (CertificateException e) {
throw new SecurityException(e);
}
return ks;
}
@Override
public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
config = null;
setup();
}
@Override
public Credential getCredential(String keyName) {
try {
CriteriaSet cs = new CriteriaSet();
EntityIDCriteria criteria = new EntityIDCriteria(keyName);
cs.add(criteria);
return resolveSingle(cs);
} catch (org.opensaml.xml.security.SecurityException e) {
throw new SAMLRuntimeException("Can't obtain SP signing key", e);
}
}
@Override
public Set<String> getAvailableCredentials() {
if (availableCredentials != null) {
return availableCredentials;
}
try {
availableCredentials = new HashSet<>();
Enumeration<String> aliases = keyStore.aliases();
while (aliases.hasMoreElements()) {
availableCredentials.add(aliases.nextElement());
}
return availableCredentials;
} catch (KeyStoreException e) {
throw new RuntimeException("Unable to load aliases from keyStore", e);
}
}
public X509Certificate getCertificate(String alias) {
if (alias == null || alias.length() == 0) {
return null;
}
try {
return (X509Certificate) keyStore.getCertificate(alias);
} catch (KeyStoreException e) {
log.error("Error loading certificate", e);
}
return null;
}
@Override
public Credential getSigningCredential() {
if (!hasCredentials() || config.getSigningKey() == null) {
return null;
}
return getCredential(config.getSigningKey());
}
@Override
public Credential getEncryptionCredential() {
if (!hasCredentials() || config.getEncryptionKey() == null) {
return null;
}
return getCredential(config.getEncryptionKey());
}
@Override
public Credential getTlsCredential() {
if (!hasCredentials() || config.getTlsKey() == null) {
return null;
}
return getCredential(config.getTlsKey());
}
@Override
public Iterable<Credential> resolve(CriteriaSet criteria) throws org.opensaml.xml.security.SecurityException {
return credentialResolver.resolve(criteria);
}
@Override
public Credential resolveSingle(CriteriaSet criteria) throws SecurityException {
return credentialResolver.resolveSingle(criteria);
}
private boolean hasCredentials() {
return config != null && credentialResolver != null;
}
}