Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

246 Enhance the SslConfig class: #247

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ and the rest of the path). However, if your secret path is prefixed, such that

SSL Config
----------
If your Vault server uses a SSL certificate, then you must supply that certificate to establish connections. Also, if
If your Vault server uses an SSL certificate, then you must supply that certificate to establish connections. Also, if
you are using certificate-based client authentication, then you must supply a client certificate and private key that
have been previously registered with your Vault server.

Expand All @@ -149,6 +149,13 @@ To disable SSL certificate verification altogether, set `sslVerify(false)`. YOU
PRODUCTION SETTING! However, it can be useful in a development or testing server context. If this value is
explicitly set to `false`, then all other SSL config is basically unused.

```
.provider(provider) // Defaults to using the default Java security provider
```

To build the SSLContext, the `SslConfig` class uses the default Java security provider. You can override this by
supplying the name of a custom security provider.

#### Java Keystore (JKS) based config

You can provide the driver with a JKS truststore, containing Vault's server-side certificate for basic SSL,
Expand Down Expand Up @@ -180,6 +187,8 @@ NOTE: JKS-based config trumps PEM-based config (see below). If for some reason
with both JKS and PEM data present, then only the JKS data will be used. You cannot "mix-and-match", providing
a JKS-based truststore and PEM-based client auth data.

`.sslContext(SSLContext)` - If you want to initialize the SSLContext yourself, you can supply it directly.

#### OpenSSL (PEM) based config

To supply Vault's server-side certificate for basic SSL, you can use one of the following three options:
Expand Down
60 changes: 44 additions & 16 deletions src/main/java/com/bettercloud/vault/SslConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.UnrecoverableKeyException;
import java.security.cert.Certificate;
Expand Down Expand Up @@ -52,6 +53,7 @@ public class SslConfig implements Serializable {
private String pemUTF8; // exposed to unit tests
private String clientPemUTF8;
private String clientKeyPemUTF8;
private String provider;
private Boolean verifyObject;
private EnvironmentLoader environmentLoader;

Expand Down Expand Up @@ -430,6 +432,26 @@ public SslConfig clientKeyPemResource(final String classpathResource) throws Vau
return this;
}

/**
* <p>It is possible to override the default Java security provider used to obtain instances of the various security constructs (SSLContext, etc) by supplying its name.
* @param provider The name of a {@link java.security.Provider}</p> used to obtain the SSLContext instance.
* @return This object, with provider populated, ready for additional builder-pattern method calls or else finalization with the build() method
*/
public SslConfig provider(String provider) {
this.provider = provider;
return this;
}

/**
* <p>Supply a pre-built {@link SSLContext}</p>
* @param sslContext an instance of SSLContext
* @return This object, with sslContext populated, ready for finalization with the {@link #build()} method
*/
public SslConfig sslContext(SSLContext sslContext) {
this.sslContext = sslContext;
return this;
}

/**
* <p>This is the terminating method in the builder pattern. The method that validates all of the fields that
* has been set already, uses environment variables when available to populate any unset fields, and returns
Expand Down Expand Up @@ -505,30 +527,32 @@ private SSLContext buildSslContextFromJks() throws VaultException {
TrustManager[] trustManagers = null;
if (trustStore != null) {
try {
final TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
String algorithm = TrustManagerFactory.getDefaultAlgorithm();
final TrustManagerFactory trustManagerFactory = provider != null ? TrustManagerFactory.getInstance(algorithm, provider) : TrustManagerFactory.getInstance(algorithm);
trustManagerFactory.init(trustStore);
trustManagers = trustManagerFactory.getTrustManagers();
} catch (NoSuchAlgorithmException | KeyStoreException e) {
} catch (NoSuchAlgorithmException | KeyStoreException | NoSuchProviderException e) {
throw new VaultException(e);
}
}

KeyManager[] keyManagers = null;
if (keyStore != null) {
try {
final KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
String algorithm = KeyManagerFactory.getDefaultAlgorithm();
final KeyManagerFactory keyManagerFactory = provider != null ? KeyManagerFactory.getInstance(algorithm, provider) : KeyManagerFactory.getInstance(algorithm);
keyManagerFactory.init(keyStore, keyStorePassword == null ? null : keyStorePassword.toCharArray());
keyManagers = keyManagerFactory.getKeyManagers();
} catch (NoSuchAlgorithmException | UnrecoverableKeyException | KeyStoreException e) {
} catch (NoSuchAlgorithmException | UnrecoverableKeyException | KeyStoreException | NoSuchProviderException e) {
throw new VaultException(e);
}
}

try {
final SSLContext sslContext = SSLContext.getInstance("TLS");
final SSLContext sslContext = provider != null ? SSLContext.getInstance("TLS", provider) : SSLContext.getInstance("TLS");
sslContext.init(keyManagers, trustManagers, null);
return sslContext;
} catch (NoSuchAlgorithmException | KeyManagementException e) {
} catch (NoSuchAlgorithmException | KeyManagementException | NoSuchProviderException e) {
throw new VaultException(e);
}
}
Expand All @@ -541,26 +565,29 @@ private SSLContext buildSslContextFromJks() throws VaultException {
*/
private SSLContext buildSslContextFromPem() throws VaultException {
try {
final CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
final CertificateFactory certificateFactory = provider != null ? CertificateFactory.getInstance("X.509", provider) : CertificateFactory.getInstance("X.509");

TrustManager[] trustManagers = null;
if (pemUTF8 != null) {
final TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
String algorithm = TrustManagerFactory.getDefaultAlgorithm();
final TrustManagerFactory trustManagerFactory = provider != null ? TrustManagerFactory.getInstance(algorithm, provider) : TrustManagerFactory.getInstance(algorithm);
// Convert the trusted servers PEM data into an X509Certificate
X509Certificate certificate;
try (final ByteArrayInputStream pem = new ByteArrayInputStream(pemUTF8.getBytes(StandardCharsets.UTF_8))) {
certificate = (X509Certificate) certificateFactory.generateCertificate(pem);
}
// Build a truststore
final KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
String type = KeyStore.getDefaultType();
final KeyStore keyStore = provider != null ? KeyStore.getInstance(type, provider) : KeyStore.getInstance(type);
keyStore.load(null);
keyStore.setCertificateEntry("caCert", certificate);
trustManagerFactory.init(keyStore);
trustManagers = trustManagerFactory.getTrustManagers();
}
KeyManager[] keyManagers = null;
if (clientPemUTF8 != null && clientKeyPemUTF8 != null) {
final KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
String algorithm = KeyManagerFactory.getDefaultAlgorithm();
final KeyManagerFactory keyManagerFactory = provider != null ? KeyManagerFactory.getInstance(algorithm, provider) : KeyManagerFactory.getInstance(algorithm);
// Convert the client certificate PEM data into an X509Certificate
X509Certificate clientCertificate;
try (final ByteArrayInputStream pem = new ByteArrayInputStream(clientPemUTF8.getBytes(StandardCharsets.UTF_8))) {
Expand All @@ -572,22 +599,23 @@ private SSLContext buildSslContextFromPem() throws VaultException {
.replace("-----END PRIVATE KEY-----", "");
final byte[] keyBytes = Base64.getMimeDecoder().decode(strippedKey);
final PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes);
final KeyFactory factory = KeyFactory.getInstance("RSA");
final KeyFactory factory = provider != null ? KeyFactory.getInstance("RSA", provider) : KeyFactory.getInstance("RSA");
final PrivateKey privateKey = factory.generatePrivate(pkcs8EncodedKeySpec);

// Build a keystore
final KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
String type = KeyStore.getDefaultType();
final KeyStore keyStore = provider != null ? KeyStore.getInstance(type, provider) : KeyStore.getInstance(type);
keyStore.load(null, "password".toCharArray());
keyStore.setCertificateEntry("clientCert", clientCertificate);
keyStore.setKeyEntry("key", privateKey, "password".toCharArray(), new Certificate[] { clientCertificate });
keyManagerFactory.init(keyStore, "password".toCharArray());
keyManagers = keyManagerFactory.getKeyManagers();
}

final SSLContext sslContext = SSLContext.getInstance("TLS");
final SSLContext sslContext = provider != null ? SSLContext.getInstance("TLS", provider) : SSLContext.getInstance("TLS");
sslContext.init(keyManagers, trustManagers, null);
return sslContext;
} catch (CertificateException | IOException | NoSuchAlgorithmException | KeyStoreException | KeyManagementException | UnrecoverableKeyException | InvalidKeySpecException e) {
} catch (CertificateException | IOException | NoSuchAlgorithmException | KeyStoreException | KeyManagementException | UnrecoverableKeyException | InvalidKeySpecException | NoSuchProviderException e) {
throw new VaultException(e);
}
}
Expand All @@ -606,10 +634,10 @@ private SSLContext buildSslContextFromPem() throws VaultException {
*/
private KeyStore inputStreamToKeyStore(final InputStream inputStream, final String password) throws VaultException {
try {
final KeyStore keyStore = KeyStore.getInstance("JKS");
final KeyStore keyStore = provider != null ? KeyStore.getInstance("JKS", provider) : KeyStore.getInstance("JKS");
keyStore.load(inputStream, password == null ? null : password.toCharArray());
return keyStore;
} catch (KeyStoreException | CertificateException | NoSuchAlgorithmException | IOException e) {
} catch (KeyStoreException | CertificateException | NoSuchAlgorithmException | IOException | NoSuchProviderException e) {
throw new VaultException(e);
}
}
Expand Down