/*
* Copyright (c) 2016 OBiBa. All rights reserved.
*
* This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.obiba.ssl;
import java.net.Socket;
import java.security.KeyPair;
import java.security.KeyStoreException;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Set;
import javax.annotation.Nullable;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.X509ExtendedKeyManager;
import org.obiba.security.KeyStoreManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* An implementation of {@code X509ExtendedKeyManager} on {@link org.obiba.security.KeyStoreManager}. This implementation will list all available key
* pairs in the system keystore and select the first one that matches the requested algorithm.
*/
public class X509ExtendedKeyManagerImpl extends X509ExtendedKeyManager {
private static final Logger log = LoggerFactory.getLogger(X509ExtendedKeyManagerImpl.class);
/**
* The key alias to use first when looking up keypairs for serving HTTPs.
*/
public static final String HTTPS_ALIAS = "https";
private final KeyStoreManager keyStoreManager;
public X509ExtendedKeyManagerImpl(KeyStoreManager keyStoreManager) {
if(keyStoreManager == null) throw new IllegalArgumentException("KeyStoreManager cannot be null");
this.keyStoreManager = keyStoreManager;
}
@Override
public String chooseClientAlias(String[] keyTypes, Principal[] issuers, @Nullable Socket socket) {
log.debug("chooseClientAlias({}, {}, socket)", keyTypes, issuers);
for(String keyType : keyTypes) {
if(isKeyType(HTTPS_ALIAS, keyType)) {
return HTTPS_ALIAS;
}
String alias = chooseServerAlias(keyType, issuers, socket);
if(alias != null) {
return alias;
}
}
return null;
}
@Override
public String chooseServerAlias(String keyType, Principal[] issuers, @Nullable Socket socket) {
log.debug("Requested keyType: '{}'", keyType);
if(isKeyType(HTTPS_ALIAS, keyType)) {
log.debug("Selecting key '{}'", HTTPS_ALIAS);
return HTTPS_ALIAS;
}
for(String alias : keyStoreManager.listKeyPairs()) {
KeyPair pair = keyStoreManager.getKeyPair(alias);
if(pair.getPrivate().getAlgorithm().equals(keyType)) {
log.debug("Selecting key '{}'", alias);
return alias;
}
}
log.debug("No appropriate key pair found for SSL.");
return null;
}
@Override
public X509Certificate[] getCertificateChain(String alias) {
log.debug("getCertificateChain({})", alias);
try {
Certificate[] certs = keyStoreManager.getKeyStore().getCertificateChain(alias);
// Convert Certificate[] to X509Certificate[]
return Arrays.copyOf(certs, certs.length, X509Certificate[].class);
} catch(KeyStoreException e) {
throw new RuntimeException(e);
}
}
@Nullable
@Override
public String[] getClientAliases(String keyType, Principal[] issuers) {
log.debug("getClientAliases({}, {})", keyType, issuers);
return null;
}
@Override
public PrivateKey getPrivateKey(String alias) {
log.debug("getPrivateKey({})", alias);
return keyStoreManager.getKeyPair(alias).getPrivate();
}
@Override
public String[] getServerAliases(String keyType, Principal[] issuers) {
log.debug("getServerAliases({}, {})", keyType, issuers);
Set<String> keyPairs = keyStoreManager.listKeyPairs();
return keyPairs.toArray(new String[keyPairs.size()]);
}
@Override
public String chooseEngineClientAlias(String[] keyTypes, Principal[] issuers, SSLEngine engine) {
log.debug("chooseEngineClientAlias({}, {})", keyTypes, issuers);
return chooseClientAlias(keyTypes, issuers, null);
}
@Override
public String chooseEngineServerAlias(String keyType, Principal[] issuers, SSLEngine engine) {
log.debug("chooseEngineServerAlias({}, {})", keyType, Arrays.toString(issuers));
return chooseServerAlias(keyType, issuers, null);
}
/**
* Returns true if the key {@code alias} exists and its algorithm is {@code keyType}
*
* @param alias
* @param keyType
* @return
*/
private boolean isKeyType(String alias, String keyType) {
return keyStoreManager.hasKeyPair(alias) && keyStoreManager.getKeyPair(alias).getPrivate().getAlgorithm().equals(keyType);
}
}