blob: bd16aa436a4f80b03d2a3e9d247d23b8b421ddd1 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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
*
* 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.
*/
package org.apache.cxf.configuration.jsse;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Paths;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.PKIXBuilderParameters;
import java.security.cert.X509CertSelector;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.net.ssl.CertPathTrustManagerParameters;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.PasswordCallback;
import org.apache.cxf.Bus;
import org.apache.cxf.BusFactory;
import org.apache.cxf.common.classloader.ClassLoaderUtils;
import org.apache.cxf.common.logging.LogUtils;
import org.apache.cxf.configuration.security.CertStoreType;
import org.apache.cxf.configuration.security.KeyManagersType;
import org.apache.cxf.configuration.security.KeyStoreType;
import org.apache.cxf.configuration.security.SecureRandomParameters;
import org.apache.cxf.configuration.security.TrustManagersType;
import org.apache.cxf.resource.ResourceManager;
/**
* This class provides some functionality to convert the JAXB
* generated types in the security.xsd to the items needed
* to programatically configure the HTTPConduit and HTTPDestination
* with TLSClientParameters and TLSServerParameters respectively.
*/
public final class TLSParameterJaxBUtils {
private static final Logger LOG =
LogUtils.getL7dLogger(TLSParameterJaxBUtils.class);
private TLSParameterJaxBUtils() {
// empty
}
/**
* This method converts the JAXB generated type into a SecureRandom.
*/
public static SecureRandom getSecureRandom(
SecureRandomParameters secureRandomParams
) throws GeneralSecurityException {
SecureRandom secureRandom = null;
if (secureRandomParams != null) {
String secureRandomAlg =
secureRandomParams.getAlgorithm();
String randomProvider =
secureRandomParams.getProvider();
if (randomProvider != null) {
secureRandom = secureRandomAlg != null
? SecureRandom.getInstance(
secureRandomAlg,
randomProvider)
: null;
} else {
secureRandom = secureRandomAlg != null
? SecureRandom.getInstance(
secureRandomAlg)
: null;
}
}
return secureRandom;
}
public static KeyStore getKeyStore(KeyStoreType kst) throws GeneralSecurityException, IOException {
return getKeyStore(kst, false);
}
/**
* This method converts a JAXB generated KeyStoreType into a KeyStore.
*/
public static KeyStore getKeyStore(KeyStoreType kst, boolean trustStore)
throws GeneralSecurityException,
IOException {
if (kst == null) {
return null;
}
final String type;
if (trustStore) {
type = SSLUtils.getTrustStoreType(kst.isSetType()
? kst.getType() : null, LOG, KeyStore.getDefaultType());
} else {
type = SSLUtils.getKeystoreType(kst.isSetType()
? kst.getType() : null, LOG, KeyStore.getDefaultType());
}
char[] password = kst.isSetPassword()
? deobfuscate(kst.getPassword())
: null;
if (password == null) {
final String tmp;
if (trustStore) {
tmp = SSLUtils.getTruststorePassword(null, LOG);
} else {
tmp = SSLUtils.getKeystorePassword(null, LOG);
}
if (tmp != null) {
password = tmp.toCharArray();
}
}
final String provider;
if (trustStore) {
provider = SSLUtils.getTruststoreProvider(kst.isSetProvider() ? kst.getProvider() : null, LOG);
} else {
provider = SSLUtils.getKeystoreProvider(kst.isSetProvider() ? kst.getProvider() : null, LOG);
}
KeyStore keyStore = provider == null
? KeyStore.getInstance(type)
: KeyStore.getInstance(type, provider);
if (kst.isSetFile()) {
try (InputStream kstInputStream = Files.newInputStream(Paths.get(kst.getFile()))) {
keyStore.load(kstInputStream, password);
}
} else if (kst.isSetResource()) {
final InputStream is = getResourceAsStream(kst.getResource());
if (is == null) {
final String msg =
"Could not load keystore resource " + kst.getResource();
LOG.severe(msg);
throw new IOException(msg);
}
keyStore.load(is, password);
} else if (kst.isSetUrl()) {
keyStore.load(new URL(kst.getUrl()).openStream(), password);
} else {
final String loc;
if (trustStore) {
loc = SSLUtils.getTruststore(null, LOG);
} else {
loc = SSLUtils.getKeystore(null, LOG);
}
if (loc != null) {
try (InputStream ins = Files.newInputStream(Paths.get(loc))) {
keyStore.load(ins, password);
} catch (NoSuchFileException ex) {
// Fall back to load the location as a stream
try (InputStream ins = getResourceAsStream(loc)) {
keyStore.load(ins, password);
}
}
}
}
return keyStore;
}
/**
* This method converts a JAXB generated CertStoreType into a KeyStore.
*/
public static KeyStore getKeyStore(final CertStoreType pst)
throws IOException, CertificateException, KeyStoreException, NoSuchAlgorithmException {
if (pst == null) {
return null;
}
String type;
if (pst.isSetType()) {
type = pst.getType();
} else {
type = KeyStore.getDefaultType();
}
if (pst.isSetFile()) {
InputStream is = Files.newInputStream(Paths.get(pst.getFile()));
return createTrustStore(is, type);
}
if (pst.isSetResource()) {
final InputStream is = getResourceAsStream(pst.getResource());
if (is == null) {
final String msg =
"Could not load truststore resource " + pst.getResource();
LOG.severe(msg);
throw new IOException(msg);
}
return createTrustStore(is, type);
}
if (pst.isSetUrl()) {
return createTrustStore(new URL(pst.getUrl()).openStream(), type);
}
throw new IllegalArgumentException("Could not create KeyStore based on information in CertStoreType");
}
private static InputStream getResourceAsStream(String resource) {
InputStream is = ClassLoaderUtils.getResourceAsStream(resource, TLSParameterJaxBUtils.class);
if (is == null) {
Bus bus = BusFactory.getThreadDefaultBus(true);
ResourceManager rm = bus.getExtension(ResourceManager.class);
if (rm != null) {
is = rm.getResourceAsStream(resource);
}
}
return is;
}
/**
* Create a KeyStore containing the trusted CA certificates contained
* in the supplied input stream.
*/
private static KeyStore createTrustStore(final InputStream is, String type)
throws IOException, CertificateException, KeyStoreException, NoSuchAlgorithmException {
final Collection<? extends Certificate> certs = loadCertificates(is);
final KeyStore keyStore =
KeyStore.getInstance(type);
keyStore.load(null, null);
for (Certificate cert : certs) {
final X509Certificate xcert = (X509Certificate) cert;
keyStore.setCertificateEntry(
xcert.getSubjectX500Principal().getName(),
cert
);
}
return keyStore;
}
/**
* load the certificates as X.509 certificates
*/
private static Collection<? extends Certificate> loadCertificates(final InputStream is)
throws IOException, CertificateException {
final CertificateFactory factory = CertificateFactory.getInstance("X.509");
return factory.generateCertificates(is);
}
private static char[] deobfuscate(String s) {
// From the Jetty org.eclipse.jetty.http.security.Password class
if (!s.startsWith("OBF:")) {
return s.toCharArray();
}
s = s.substring(4);
char[] b = new char[s.length() / 2];
int l = 0;
for (int i = 0; i < s.length(); i += 4) {
String x = s.substring(i, i + 4);
int i0 = Integer.parseInt(x, 36);
int i1 = i0 / 256;
int i2 = i0 % 256;
b[l++] = (char) ((i1 + i2 - 254) / 2);
}
return new String(b, 0, l).toCharArray();
}
/**
* This method converts the JAXB KeyManagersType into a list of
* JSSE KeyManagers.
*/
public static KeyManager[] getKeyManagers(KeyManagersType kmc)
throws GeneralSecurityException,
IOException {
KeyStore keyStore = getKeyStore(kmc.getKeyStore(), false);
String alg = kmc.isSetFactoryAlgorithm()
? kmc.getFactoryAlgorithm()
: KeyManagerFactory.getDefaultAlgorithm();
char[] keyPass = getKeyPassword(kmc);
KeyManagerFactory fac =
kmc.isSetProvider()
? KeyManagerFactory.getInstance(alg, kmc.getProvider())
: KeyManagerFactory.getInstance(alg);
fac.init(keyStore, keyPass);
return fac.getKeyManagers();
}
/**
* This method converts the JAXB KeyManagersType into a list of
* JSSE KeyManagers.
*/
public static KeyManager[] getKeyManagers(KeyManagersType kmc, String alias)
throws GeneralSecurityException,
IOException {
KeyStore keyStore = getKeyStore(kmc.getKeyStore(), false);
String alg = kmc.isSetFactoryAlgorithm()
? kmc.getFactoryAlgorithm()
: KeyManagerFactory.getDefaultAlgorithm();
char[] keyPass = getKeyPassword(kmc);
KeyManagerFactory fac =
kmc.isSetProvider()
? KeyManagerFactory.getInstance(alg, kmc.getProvider())
: KeyManagerFactory.getInstance(alg);
try {
fac.init(keyStore, keyPass);
return fac.getKeyManagers();
} catch (java.security.UnrecoverableKeyException uke) {
//jsse has the restriction that different key in keystore
//cannot has different password, use MultiKeyPasswordKeyManager
//as fallback when this happen
MultiKeyPasswordKeyManager manager
= new MultiKeyPasswordKeyManager(keyStore, alias,
new String(keyPass));
return new KeyManager[]{manager};
}
}
private static char[] getKeyPassword(KeyManagersType kmc) {
char[] keyPass = kmc.isSetKeyPassword()
? deobfuscate(kmc.getKeyPassword())
: null;
if (keyPass != null) {
return keyPass;
}
String callbackHandlerClass = kmc.getKeyPasswordCallbackHandler();
if (callbackHandlerClass == null) {
return null;
}
try {
final CallbackHandler ch = (CallbackHandler) ClassLoaderUtils
.loadClass(callbackHandlerClass, TLSParameterJaxBUtils.class).newInstance();
String prompt = kmc.getKeyStore().getFile();
if (prompt == null) {
prompt = kmc.getKeyStore().getResource();
}
PasswordCallback pwCb = new PasswordCallback(prompt, false);
PasswordCallback[] callbacks = new PasswordCallback[] {pwCb};
ch.handle(callbacks);
keyPass = callbacks[0].getPassword();
} catch (Exception e) {
LOG.log(Level.WARNING,
"Cannot load key password from callback handler: " + e.getMessage(), e);
}
return keyPass;
}
/**
* This method converts the JAXB TrustManagersType into a list of
* JSSE TrustManagers.
*/
@Deprecated
public static TrustManager[] getTrustManagers(TrustManagersType tmc)
throws GeneralSecurityException,
IOException {
return getTrustManagers(tmc, false);
}
public static TrustManager[] getTrustManagers(TrustManagersType tmc, boolean enableRevocation)
throws GeneralSecurityException,
IOException {
final KeyStore keyStore =
tmc.isSetKeyStore()
? getKeyStore(tmc.getKeyStore(), true)
: (tmc.isSetCertStore()
? getKeyStore(tmc.getCertStore())
: null);
String alg = tmc.isSetFactoryAlgorithm()
? tmc.getFactoryAlgorithm()
: TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory fac =
tmc.isSetProvider()
? TrustManagerFactory.getInstance(alg, tmc.getProvider())
: TrustManagerFactory.getInstance(alg);
if (enableRevocation) {
PKIXBuilderParameters param = new PKIXBuilderParameters(keyStore, new X509CertSelector());
param.setRevocationEnabled(true);
fac.init(new CertPathTrustManagerParameters(param));
} else {
fac.init(keyStore);
}
return fac.getTrustManagers();
}
}