Skip to content

Commit

Permalink
Merge pull request #335 from lightblue-platform/certAlias
Browse files Browse the repository at this point in the history
Allow no certAlias
  • Loading branch information
paterczm committed May 30, 2017
2 parents cbc722c + 16eb1d7 commit 16129e8
Show file tree
Hide file tree
Showing 11 changed files with 319 additions and 91 deletions.
Expand Up @@ -74,7 +74,7 @@ public LightblueClientConfiguration(LightblueClientConfiguration configuration)
caFilePath = configuration.caFilePath;
certFilePath = configuration.certFilePath;
certPassword = configuration.certPassword;
certAlias = FilenameUtils.getBaseName(certFilePath);
certAlias = configuration.certAlias;
compression = configuration.compression;
readPreference = configuration.readPreference;
writeConcern = configuration.writeConcern;
Expand Down Expand Up @@ -169,7 +169,6 @@ public String getCertFilePath() {
*/
public void setCertFilePath(String certFilePath) {
this.certFilePath = certFilePath;
certAlias = FilenameUtils.getBaseName(this.certFilePath);
}

public String getCertPassword() {
Expand Down Expand Up @@ -362,4 +361,8 @@ public void setAcceptSelfSignedCert(boolean acceptSelfSignedCert) {
this.acceptSelfSignedCert = acceptSelfSignedCert;
}

public void setCertAlias(String certAlias) {
this.certAlias = certAlias;
}

}
Expand Up @@ -57,6 +57,7 @@ public final class PropertiesLightblueClientConfiguration {
private static final String CA_FILE_PATH_KEY = "caFilePath";
private static final String CERT_FILE_PATH_KEY = "certFilePath";
private static final String CERT_PASSWORD_KEY = "certPassword";
private static final String CERT_ALIAS_KEY = "certAlias";
private static final String COMPRESSION = "compression";
private static final String BASIC_AUTH_USERNAME_KEY = "basicAuthUsername";
private static final String BASIC_AUTH_PASSWORD_KEY = "basicAuthPassword";
Expand Down Expand Up @@ -190,6 +191,7 @@ public static LightblueClientConfiguration fromObject(Properties properties) {
config.setCaFilePath(properties.getProperty(CA_FILE_PATH_KEY));
config.setCertFilePath(properties.getProperty(CERT_FILE_PATH_KEY));
config.setCertPassword(properties.getProperty(CERT_PASSWORD_KEY));
config.setCertAlias(properties.getProperty(CERT_ALIAS_KEY));
config.setDataServiceURI(properties.getProperty(DATA_SERVICE_URI_KEY));
config.setMetadataServiceURI(properties.getProperty(METADATA_SERVICE_URI_KEY));
config.setAcceptSelfSignedCert(Boolean.parseBoolean(properties.getProperty(ACCEPT_SELF_SIGNED_CERT_KEY)));
Expand Down
Expand Up @@ -23,6 +23,7 @@ public void shouldCopyAllPropertiesInCopyConstructor() {
original.setCertPassword("pass");
original.setCertFilePath("certpath");
original.setCaFilePath("capath");
original.setCertAlias("certalias");

LightblueClientConfiguration copy = new LightblueClientConfiguration(original);

Expand All @@ -32,7 +33,7 @@ public void shouldCopyAllPropertiesInCopyConstructor() {
assertEquals("pass", copy.getCertPassword());
assertEquals("certpath", copy.getCertFilePath());
assertEquals("capath", copy.getCaFilePath());
assertEquals("certpath", copy.getCertAlias());
assertEquals("certalias", copy.getCertAlias());

// make sure they are copies
original.setUseCertAuth(false);
Expand All @@ -48,7 +49,7 @@ public void shouldCopyAllPropertiesInCopyConstructor() {
assertEquals("pass", copy.getCertPassword());
assertEquals("certpath", copy.getCertFilePath());
assertEquals("capath", copy.getCaFilePath());
assertEquals("certpath", copy.getCertAlias());
assertEquals("certalias", copy.getCertAlias());
}

@Test
Expand Down
Expand Up @@ -111,7 +111,7 @@ public void shouldLookup_certPassword_PropertyForCertPassword() {
@Test
public void shouldLookup_certAlias_PropertyForCertAlias() {
Properties properties = new Properties();
properties.setProperty("certFilePath", "/path/to/theCert.pkcs12");
properties.setProperty("certAlias", "theCert");

LightblueClientConfiguration config = PropertiesLightblueClientConfiguration.fromObject(properties);

Expand Down
@@ -0,0 +1,128 @@
package com.redhat.lightblue.client.http.auth;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.security.Key;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.redhat.lightblue.client.LightblueClientConfiguration;

/**
* Provides access to certificates.
*
* @author mpatercz
*
*/
public class CertificateManager {

private static final Logger LOGGER = LoggerFactory.getLogger(SslSocketFactories.class);

static final String FILE_PROTOCOL = "file://";

static List<InputStream> getCaCertFiles(List<String> caCertFilePaths) throws FileNotFoundException {
List<InputStream> caCertFiles = new ArrayList<>();

for(String caCertFilePath : caCertFilePaths) {
caCertFiles.add(loadFile(caCertFilePath));
}

return caCertFiles;
}

static Set<Certificate> getCertificates(List<InputStream> certAuthorityFiles) throws CertificateException {
Set<Certificate> caCertificates = new LinkedHashSet<>();

for(InputStream certAuthorityFile : certAuthorityFiles) {
caCertificates.add(getCertificate(certAuthorityFile));
}
return caCertificates;
}

static InputStream loadFile(String filePath) throws FileNotFoundException {
InputStream stream = loadFile(CertificateManager.class.getClassLoader(), filePath);
if (stream == null) {
throw new FileNotFoundException("Could not read certs from "+filePath);
}
return stream;
}

private static InputStream loadFile(ClassLoader classLoader, String filePath) throws FileNotFoundException {
if (filePath.startsWith(FILE_PROTOCOL)) {
return new FileInputStream(filePath.substring(FILE_PROTOCOL.length()));
}
return classLoader.getResourceAsStream(filePath);
}

static X509Certificate getCertificate(InputStream certificate)
throws CertificateException {
CertificateFactory cf = CertificateFactory.getInstance("X509");
return (X509Certificate) cf.generateCertificate(certificate);
}

static KeyStore getPkcs12KeyStore(InputStream lightblueCert, char[] certPassword)
throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException {
KeyStore ks = KeyStore.getInstance("pkcs12");
ks.load(lightblueCert, certPassword);
return ks;
}

static KeyStore getJksKeyStore(Set<Certificate> caCertFiles, KeyStore lightblueCertKeystore,
String lightblueCertAlias, char[] lightblueCertPassword)
throws CertificateException, NoSuchAlgorithmException, IOException, KeyStoreException,
UnrecoverableKeyException {
KeyStore jks = KeyStore.getInstance("jks");

jks.load(null, lightblueCertPassword);
for(Certificate caCertFile : caCertFiles) {
jks.setCertificateEntry(caCertFile.toString(), caCertFile);
}

Certificate[] chain;
Key key ;

if (lightblueCertAlias != null) {
LOGGER.debug("Loading certificates using alias='"+lightblueCertAlias+"'");
chain = lightblueCertKeystore.getCertificateChain(lightblueCertAlias);
key = lightblueCertKeystore.getKey(lightblueCertAlias, lightblueCertPassword);

if (chain == null || key == null) {
throw new RuntimeException("Specified alias='"+lightblueCertAlias+"' does not appear to exist in the keystore.");
}
} else {
LOGGER.debug("Certificate alias not specified");

List<String> aliases = Collections.list(lightblueCertKeystore.aliases());

if (aliases.size() == 1) {
String alias = aliases.get(0);
LOGGER.debug("Certificate alias was not specified, but only one alias exist is the keystore. Using alias='"+alias+"'");
chain = lightblueCertKeystore.getCertificateChain(alias);
key = lightblueCertKeystore.getKey(alias, lightblueCertPassword);
} else {
throw new RuntimeException("Certificate alias not specified and the keystore has more than one alias or keystore is empty. Aliases found: "+aliases);
}
}

jks.setKeyEntry("anykey", key, lightblueCertPassword, chain);

return jks;
}

}
@@ -1,24 +1,15 @@
package com.redhat.lightblue.client.http.auth;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Paths;
import java.security.Key;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
Expand All @@ -38,12 +29,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.redhat.lightblue.client.LightblueClient;
import com.redhat.lightblue.client.LightblueClientConfiguration;
import com.redhat.lightblue.client.LightblueException;
import com.redhat.lightblue.client.PropertiesLightblueClientConfiguration;
import com.redhat.lightblue.client.http.LightblueHttpClient;
import com.redhat.lightblue.client.request.metadata.MetadataGetEntityNamesRequest;

public class SslSocketFactories {
private static final Logger LOGGER = LoggerFactory.getLogger(SslSocketFactories.class);
Expand All @@ -52,7 +38,6 @@ public class SslSocketFactories {

private static final String[] SUPPORTED_PROTOCOLS = new String[]{TLSV1};
private static final String[] SUPPORTED_CIPHER_SUITES = null;
private static final String FILE_PROTOCOL = "file://";

/**
* @return A default SSL socket factory based on whether or not the
Expand All @@ -65,8 +50,11 @@ public static SSLConnectionSocketFactory fromLightblueClientConfig(LightblueClie
KeyStoreException, KeyManagementException, IOException {
if (config.useCertAuth()) {
validateLightblueClientConfigForCertAuth(config);
return defaultCertAuthSocketFactory(getCaCertFiles(config), loadFile(config.getCertFilePath()),
config.getCertPassword().toCharArray(), config.getCertAlias(), config.isAcceptSelfSignedCert());

return defaultCertAuthSocketFactory(
CertificateManager.getCaCertFiles(config.getCaFilePaths()),
CertificateManager.loadFile(config.getCertFilePath()),
config.getCertPassword().toCharArray(), config.getCertAlias(), config.isAcceptSelfSignedCert());
}

return defaultNoAuthSocketFactory();
Expand Down Expand Up @@ -102,9 +90,9 @@ public static SSLConnectionSocketFactory defaultCertAuthSocketFactory(
throws KeyStoreException, CertificateException, IOException, NoSuchAlgorithmException,
UnrecoverableKeyException, KeyManagementException {

Set<Certificate> certificates = getCertificates(certAuthorityFiles);
KeyStore pkcs12KeyStore = getPkcs12KeyStore(authCert, authCertPassword);
KeyStore sunKeyStore = getJksKeyStore(certificates, pkcs12KeyStore, authCertAlias, authCertPassword);
Set<Certificate> certificates = CertificateManager.getCertificates(certAuthorityFiles);
KeyStore pkcs12KeyStore = CertificateManager.getPkcs12KeyStore(authCert, authCertPassword);
KeyStore sunKeyStore = CertificateManager.getJksKeyStore(certificates, pkcs12KeyStore, authCertAlias, authCertPassword);
SSLContext sslContext = getSSLContext(sunKeyStore, pkcs12KeyStore, authCertPassword, acceptSelfSignedCert);

return new SSLConnectionSocketFactory(sslContext, SUPPORTED_PROTOCOLS, SUPPORTED_CIPHER_SUITES,
Expand All @@ -116,21 +104,12 @@ public static SSLSocketFactory javaNetSslSocketFactory(LightblueClientConfigurat
UnrecoverableKeyException, KeyManagementException {
validateLightblueClientConfigForCertAuth(config);

return javaNetSslSocketFactory(getCaCertFiles(config), loadFile(config.getCertFilePath()),
return javaNetSslSocketFactory(
CertificateManager.getCaCertFiles(config.getCaFilePaths()),
CertificateManager.loadFile(config.getCertFilePath()),
config.getCertPassword().toCharArray(), config.getCertAlias(), config.isAcceptSelfSignedCert());
}

private static List<InputStream> getCaCertFiles(LightblueClientConfiguration config) throws FileNotFoundException {
List<String> caCertFilePaths = config.getCaFilePaths();
List<InputStream> caCertFiles = new ArrayList<>();

for(String caCertFilePath : caCertFilePaths) {
caCertFiles.add(loadFile(caCertFilePath));
}

return caCertFiles;
}

public static SSLSocketFactory javaNetSslSocketFactory(List<InputStream> certAuthorityFiles, InputStream authCert,
char[] authCertPassword, String authCertAlias, boolean acceptSelfSignedCert)
throws CertificateException, NoSuchAlgorithmException,
Expand All @@ -143,65 +122,14 @@ public static SSLSocketFactory javaNetSslSocketFactory(List<InputStream> certAut
+ "the certificate file is on the classpath or defined on the file system using the "
+ "'file://' prefix.");

Set<Certificate> caCertificates = getCertificates(certAuthorityFiles);
Set<Certificate> caCertificates = CertificateManager.getCertificates(certAuthorityFiles);

KeyStore pkcs12KeyStore = getPkcs12KeyStore(authCert, authCertPassword);
KeyStore sunKeyStore = getJksKeyStore(caCertificates, pkcs12KeyStore, authCertAlias, authCertPassword);
KeyStore pkcs12KeyStore = CertificateManager.getPkcs12KeyStore(authCert, authCertPassword);
KeyStore sunKeyStore = CertificateManager.getJksKeyStore(caCertificates, pkcs12KeyStore, authCertAlias, authCertPassword);
SSLContext sslContext = getSSLContext(sunKeyStore, pkcs12KeyStore, authCertPassword, acceptSelfSignedCert);
return sslContext.getSocketFactory();
}

private static Set<Certificate> getCertificates(List<InputStream> certAuthorityFiles) throws CertificateException {
Set<Certificate> caCertificates = new LinkedHashSet<>();

for(InputStream certAuthorityFile : certAuthorityFiles) {
caCertificates.add(getCertificate(certAuthorityFile));
}
return caCertificates;
}

private static InputStream loadFile(String filePath) throws FileNotFoundException {
return loadFile(SslSocketFactories.class.getClassLoader(), filePath);
}

private static InputStream loadFile(ClassLoader classLoader, String filePath) throws FileNotFoundException {
if (filePath.startsWith(FILE_PROTOCOL)) {
return new FileInputStream(filePath.substring(FILE_PROTOCOL.length()));
}
return classLoader.getResourceAsStream(filePath);
}

private static X509Certificate getCertificate(InputStream certificate)
throws CertificateException {
CertificateFactory cf = CertificateFactory.getInstance("X509");
return (X509Certificate) cf.generateCertificate(certificate);
}

private static KeyStore getPkcs12KeyStore(InputStream lightblueCert, char[] certPassword)
throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException {
KeyStore ks = KeyStore.getInstance("pkcs12");
ks.load(lightblueCert, certPassword);
return ks;
}

private static KeyStore getJksKeyStore(Set<Certificate> caCertFiles, KeyStore lightblueCertKeystore,
String lightblueCertAlias, char[] lightblueCertPassword)
throws CertificateException, NoSuchAlgorithmException, IOException, KeyStoreException,
UnrecoverableKeyException {
KeyStore jks = KeyStore.getInstance("jks");

jks.load(null, lightblueCertPassword);
for(Certificate caCertFile : caCertFiles) {
jks.setCertificateEntry(caCertFile.toString(), caCertFile);
}

Certificate[] chain = lightblueCertKeystore.getCertificateChain(lightblueCertAlias);
Key key = lightblueCertKeystore.getKey(lightblueCertAlias, lightblueCertPassword);
jks.setKeyEntry("anykey", key, lightblueCertPassword, chain);

return jks;
}

/**
* Naive trust manager trusts all.
*
Expand Down Expand Up @@ -255,5 +183,7 @@ private static void validateLightblueClientConfigForCertAuth(LightblueClientConf
if (StringUtils.isBlank(config.getCertPassword())) {
throw new IllegalArgumentException("Must provide a certPassword.");
}

// certAlias is not required if only one cert exists in the keystore (usually the case)
}
}

0 comments on commit 16129e8

Please sign in to comment.