Remove mutual TLS configuration, in favor of the Cloud Foundry Java Buildpack's container security provider support.

This commit is contained in:
Scott Frederick
2017-05-31 17:14:36 -05:00
parent 70393b7345
commit 9ffed3dbbb
6 changed files with 8 additions and 252 deletions

View File

@@ -39,11 +39,6 @@
<artifactId>httpclient</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>

View File

@@ -19,17 +19,11 @@ package org.springframework.credhub.configuration;
import java.io.IOException;
import java.security.GeneralSecurityException;
import javax.net.ssl.SSLContext;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.springframework.credhub.support.ClientOptions;
import org.springframework.credhub.support.SslConfiguration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.util.Assert;
@@ -50,23 +44,19 @@ public class ClientHttpRequestFactoryFactory {
ClientHttpRequestFactoryFactory.class.getClassLoader());
/**
* Create a {@link ClientHttpRequestFactory} for the given {@link ClientOptions} and
* {@link SslConfiguration}.
* Create a {@link ClientHttpRequestFactory} for the given {@link ClientOptions}.
*
* @param options must not be {@literal null}
* @param sslConfiguration must not be {@literal null}
* @return a new {@link ClientHttpRequestFactory}. Lifecycle beans must be initialized
* after obtaining.
*/
public static ClientHttpRequestFactory create(ClientOptions options,
SslConfiguration sslConfiguration) {
public static ClientHttpRequestFactory create(ClientOptions options) {
Assert.notNull(options, "ClientOptions must not be null");
Assert.notNull(sslConfiguration, "SslConfiguration must not be null");
try {
if (HTTP_COMPONENTS_PRESENT) {
return HttpComponents.usingHttpComponents(options, sslConfiguration);
return HttpComponents.usingHttpComponents(options);
}
}
catch (GeneralSecurityException e) {
@@ -79,36 +69,16 @@ public class ClientHttpRequestFactoryFactory {
throw new IllegalStateException("Only Apache HTTP Components is supported.");
}
private static boolean hasSslConfiguration(SslConfiguration sslConfiguration) {
return sslConfiguration.getTrustStore() != null
|| sslConfiguration.getKeyStore() != null;
}
/**
* {@link ClientHttpRequestFactory} for Apache HttpComponents.
*/
static class HttpComponents {
static ClientHttpRequestFactory usingHttpComponents(ClientOptions options,
SslConfiguration sslConfiguration)
static ClientHttpRequestFactory usingHttpComponents(ClientOptions options)
throws GeneralSecurityException, IOException {
HttpClientBuilder httpClientBuilder = HttpClients.custom();
if (hasSslConfiguration(sslConfiguration)) {
SSLContext sslContext = SSLContexts.custom()
.loadTrustMaterial(sslConfiguration.getKeyStore(),
new TrustSelfSignedStrategy())
.loadKeyMaterial(sslConfiguration.getTrustStore(),
sslConfiguration.getKeyPassword())
.build();
SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(
sslContext);
httpClientBuilder.setSSLSocketFactory(sslSocketFactory);
httpClientBuilder.setSSLContext(sslContext);
}
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(options.getConnectionTimeout())
.setSocketTimeout(options.getReadTimeout())

View File

@@ -24,7 +24,6 @@ import org.springframework.credhub.core.CloudFoundryAppInstanceProperties;
import org.springframework.credhub.core.CredHubProperties;
import org.springframework.credhub.core.CredHubTemplate;
import org.springframework.credhub.support.ClientOptions;
import org.springframework.credhub.support.SslConfiguration;
import org.springframework.http.client.ClientHttpRequestFactory;
/**
@@ -84,18 +83,17 @@ public class CredHubConfiguration {
* Create a {@link ClientFactoryWrapper} containing a
* {@link ClientHttpRequestFactory}. {@link ClientHttpRequestFactory} is not exposed
* as root bean because {@link ClientHttpRequestFactory} is configured with
* {@link ClientOptions} and {@link SslConfiguration} which are not necessarily
* {@link ClientOptions} which are not necessarily
* applicable for the whole application.
*
* @return the {@link ClientFactoryWrapper} to wrap a {@link ClientHttpRequestFactory}
* instance.
* @see #clientOptions()
* @see #sslConfiguration()
*/
@Bean
public ClientFactoryWrapper clientHttpRequestFactoryWrapper() {
ClientHttpRequestFactory clientHttpRequestFactory =
ClientHttpRequestFactoryFactory.create(clientOptions(), sslConfiguration());
ClientHttpRequestFactoryFactory.create(clientOptions());
return new ClientFactoryWrapper(clientHttpRequestFactory);
}
@@ -108,20 +106,6 @@ public class CredHubConfiguration {
return new ClientOptions();
}
/**
* Create the default {@link SslConfiguration} to configure SSL context parameters.
* The default configuration uses a certificate and key supplied in the application
* container to configure mutual SSL authentication between the client application
* and the CredHub server.
*
* @return the default {@link SslConfiguration}
*/
private SslConfiguration sslConfiguration() {
CloudFoundryAppInstanceProperties properties = cloudFoundryAppInstanceProperties();
return SslConfiguration.forContainerCert(properties.getInstanceCertLocation(),
properties.getInstanceKeyLocation());
}
/**
* Wrapper for {@link ClientHttpRequestFactory} to not expose the bean globally.
*/

View File

@@ -1,183 +0,0 @@
/*
* Copyright 2016-2017 the original author or authors.
*
* 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.
*/
package org.springframework.credhub.support;
import java.io.FileReader;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
/**
* Client configuration for SSL connectivity.
*/
public class SslConfiguration {
private static final char[] KEY_PASSWORD = "keystore".toCharArray();
private static final String CERTIFICATE_NAME = "credhub-cert";
private static final String KEY_NAME = "credhub-key";
private KeyStore trustStore;
private KeyStore keyStore;
/**
* Create an empty {@link SslConfiguration}. Intended for internal use.
*/
public SslConfiguration() {
}
/**
* Create an {@link SslConfiguration} that uses a certificate and private key that
* have been placed in a Cloud Foundry application container for use with mutual SSL
* authentication to CredHub.
*
* @param instanceCertLocation the absolute path of the certificate file in the app
* instance container
* @param instanceKeyLocation the absolute path of the private key file in the app
* instance container
* @return the {@link SslConfiguration} configured to use the container certificate
* and private key
*/
public static SslConfiguration forContainerCert(String instanceCertLocation,
String instanceKeyLocation) {
SslConfiguration sslConfiguration = new SslConfiguration();
KeyStore keyStore = sslConfiguration.buildKeyStore(instanceCertLocation,
instanceKeyLocation);
sslConfiguration.setKeyStore(keyStore);
sslConfiguration.setTrustStore(keyStore);
return sslConfiguration;
}
/**
* Get the {@link KeyStore key store} resource used to configure the SSL context.
*
* @return the key store
*/
public KeyStore getKeyStore() {
return keyStore;
}
private void setKeyStore(KeyStore keyStore) {
this.keyStore = keyStore;
}
/**
* Get the {@link KeyStore trust store} resource used to configure the SSL context.
*
* @return the trust store
*/
public KeyStore getTrustStore() {
return trustStore;
}
private void setTrustStore(KeyStore keyStore) {
this.trustStore = keyStore;
}
/**
* Get the password used to secure the generated key store.
*
* @return they key store password
*/
public char[] getKeyPassword() {
return KEY_PASSWORD;
}
/**
* Build a {@link KeyStore} using the container certificate and private key.
*
* @param instanceCertLocation the absolute path of the certificate file in the app
* instance container
* @param instanceKeyLocation the absolute path of the private key file in the app
* instance container
* @return the created key store
*/
private KeyStore buildKeyStore(String instanceCertLocation,
String instanceKeyLocation) {
Certificate cert = parseCertificate(instanceCertLocation);
PrivateKey key = parsePrivateKey(instanceKeyLocation);
return createKeyStore(cert, key);
}
/**
* Parse a PEM-formatted certificate and convert to a {@link Certificate}.
*
* @param certificateLocation the absolute path of the certificate file in the app
* instance container
* @return the created {@link Certificate}
*/
private Certificate parseCertificate(String certificateLocation) {
try {
PEMParser parser = new PEMParser(new FileReader(certificateLocation));
X509CertificateHolder certHolder =
(X509CertificateHolder) parser.readObject();
JcaX509CertificateConverter converter = new JcaX509CertificateConverter();
return converter.getCertificate(certHolder);
}
catch (Exception e) {
throw new IllegalArgumentException(
"Error parsing and loading certificate from location "
+ certificateLocation,
e);
}
}
/**
* Parse a PEM-formatted key and convert to a {@link PrivateKey}.
*
* @param keyLocation the absolute path of the key file in the app
* instance container
* @return the created {@link PrivateKey}
*/
private PrivateKey parsePrivateKey(String keyLocation) {
try {
PEMParser reader = new PEMParser(new FileReader(keyLocation));
PEMKeyPair key = (PEMKeyPair) reader.readObject();
JcaPEMKeyConverter converter = new JcaPEMKeyConverter();
return converter.getKeyPair(key).getPrivate();
}
catch (Exception e) {
throw new IllegalArgumentException(
"Error parsing and loading private key from location " + keyLocation,
e);
}
}
/**
* Create a {@link KeyStore} from the provided certificate and private key.
*
* @param cert the certifcate to add to the key store
* @param key the private key to add to the key store
* @return the created {@link KeyStore}
*/
private KeyStore createKeyStore(Certificate cert, PrivateKey key) {
try {
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
keystore.load(null);
keystore.setCertificateEntry(CERTIFICATE_NAME, cert);
keystore.setKeyEntry(KEY_NAME, key, KEY_PASSWORD, new Certificate[] { cert });
return keystore;
}
catch (Exception e) {
throw new IllegalArgumentException("Error creating keystore ", e);
}
}
}

View File

@@ -22,21 +22,19 @@ import org.junit.Test;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.credhub.support.ClientOptions;
import org.springframework.credhub.support.SslConfiguration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.Assert.assertThat;
import static org.springframework.credhub.configuration.ClientHttpRequestFactoryFactory.HttpComponents.usingHttpComponents;
public class ClientHttpRequestFactoryFactoryTests {
@Test
public void httpComponentsClientCreated() throws Exception {
ClientHttpRequestFactory factory =
ClientHttpRequestFactoryFactory.HttpComponents.usingHttpComponents(
new ClientOptions(), new SslConfiguration());
ClientHttpRequestFactory factory = usingHttpComponents(new ClientOptions());
assertThat(factory, instanceOf(HttpComponentsClientHttpRequestFactory.class));

View File

@@ -55,7 +55,6 @@
<properties>
<jackson.version>2.8.7</jackson.version>
<httpclient.version>4.5.3</httpclient.version>
<bouncycastle.version>1.56</bouncycastle.version>
</properties>
<dependencyManagement>
@@ -88,13 +87,6 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>${bouncycastle.version}</version>
</dependency>
</dependencies>
</dependencyManagement>