Skip to content

Commit

Permalink
Fixed #807 - added a distinction in configuration between keystore pa…
Browse files Browse the repository at this point in the history
…ssword and key manager password
  • Loading branch information
tomakehurst committed Jun 30, 2020
1 parent 500ebec commit 6ded3d6
Show file tree
Hide file tree
Showing 17 changed files with 121 additions and 29 deletions.
6 changes: 5 additions & 1 deletion docs-v2/_docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,13 @@ WireMock can accept HTTPS connections from clients, require a client to present
// Set the keystore containing the HTTPS certificate
.keystorePath("/path/to/https-certs-keystore.jks")

// Set the password to the keystore
// Set the password to the keystore. Note: the behaviour of this changed in version 2.27.0.
// Previously this set Jetty's key manager password, whereas now it sets the keystore password value.
.keystorePassword("verysecret!")

// Set the password to the Jetty's key manager. Note: added in version 2.27.0.
.keyManagerPassword("donttell")

// Set the keystore type
.keystoreType("BKS")

Expand Down
5 changes: 5 additions & 0 deletions docs-v2/_docs/running-standalone.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ certificate.

`--keystore-password`: Password to the keystore, if something other than
"password".
Note: the behaviour of this changed in version 2.27.0. Previously this set Jetty's key manager password, whereas now it
sets the keystore password value. The key manager password can be set with the (new) parameter below.

`--key-manager-password`: The password used by Jetty to access individual keys in the store, if something other than
"password".

`--https-truststore`: Path to a keystore file containing client public
certificates, proxy target public certificates & private keys to use when
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ actual request (this is how curl 7.64.1 behaves!). Neither

public static SslContextFactory.Server buildHttp2SslContextFactory(HttpsSettings httpsSettings) {
SslContextFactory.Server sslContextFactory = SslContexts.defaultSslContextFactory(httpsSettings.keyStore());
sslContextFactory.setKeyManagerPassword(httpsSettings.keyManagerPassword());
setupClientAuth(sslContextFactory, httpsSettings);
sslContextFactory.setCipherComparator(HTTP2Cipher.COMPARATOR);
sslContextFactory.setProvider("Conscrypt");
Expand Down Expand Up @@ -106,8 +107,8 @@ private static SslContextFactory.Server certificateGeneratingSslContextFactory(N
}

private static void setupKeyStore(SslContextFactory.Server sslContextFactory, KeyStoreSettings keyStoreSettings) {
sslContextFactory.setKeyStorePath(keyStoreSettings.path());
sslContextFactory.setKeyManagerPassword(keyStoreSettings.password());
sslContextFactory.setKeyStore(keyStoreSettings.loadStore());
sslContextFactory.setKeyStorePassword(keyStoreSettings.password());
sslContextFactory.setKeyStoreType(keyStoreSettings.type());
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package com.github.tomakehurst.wiremock.common;

import com.github.tomakehurst.wiremock.common.ssl.AbstractKeyStoreSource;
import com.github.tomakehurst.wiremock.common.ssl.FileOrClasspathKeyStoreSource;
import com.github.tomakehurst.wiremock.common.ssl.KeyStoreSettings;

import java.nio.file.Paths;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,18 @@ public class HttpsSettings {
private final int port;
private final String keyStorePath;
private final String keyStorePassword;
private final String keyManagerPassword;
private final String keyStoreType;
private final String trustStorePath;
private final String trustStorePassword;
private final String trustStoreType;
private final boolean needClientAuth;

public HttpsSettings(int port, String keyStorePath, String keyStorePassword, String keyStoreType, String trustStorePath, String trustStorePassword, String trustStoreType, boolean needClientAuth) {
public HttpsSettings(int port, String keyStorePath, String keyStorePassword, String keyManagerPassword, String keyStoreType, String trustStorePath, String trustStorePassword, String trustStoreType, boolean needClientAuth) {
this.port = port;
this.keyStorePath = keyStorePath;
this.keyStorePassword = keyStorePassword;
this.keyManagerPassword = keyManagerPassword;
this.keyStoreType = keyStoreType;
this.trustStorePath = trustStorePath;
this.trustStorePassword = trustStorePassword;
Expand All @@ -52,6 +54,10 @@ public String keyStorePassword() {
return keyStorePassword;
}

public String keyManagerPassword() {
return keyManagerPassword;
}

public String keyStoreType() {
return keyStoreType;
}
Expand Down Expand Up @@ -107,6 +113,7 @@ public static class Builder {
private int port;
private String keyStorePath = Resources.getResource("keystore").toString();
private String keyStorePassword = "password";
private String keyManagerPassword = "password";
private String keyStoreType = "JKS";
private String trustStorePath = null;
private String trustStorePassword = "password";
Expand All @@ -128,6 +135,11 @@ public Builder keyStorePassword(String keyStorePassword) {
return this;
}

public Builder keyManagerPassword(String keyStorePassword) {
this.keyManagerPassword = keyStorePassword;
return this;
}

public Builder keyStoreType(String keyStoreType) {
this.keyStoreType = keyStoreType;
return this;
Expand All @@ -154,7 +166,7 @@ public Builder needClientAuth(boolean needClientAuth) {
}

public HttpsSettings build() {
return new HttpsSettings(port, keyStorePath, keyStorePassword, keyStoreType, trustStorePath, trustStorePassword, trustStoreType, needClientAuth);
return new HttpsSettings(port, keyStorePath, keyStorePassword, keyManagerPassword, keyStoreType, trustStorePath, trustStorePassword, trustStoreType, needClientAuth);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
public interface Source<T> {
T load();
void save(T item);
boolean exists();
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
import com.google.common.io.Resources;

import java.io.*;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
Expand All @@ -19,7 +23,7 @@
import static java.nio.file.attribute.PosixFilePermission.*;
import static java.nio.file.attribute.PosixFilePermissions.asFileAttribute;

public class FileOrClasspathKeyStoreSource extends AbstractKeyStoreSource {
public class FileOrClasspathKeyStoreSource extends KeyStoreSource {

private final String path;

Expand All @@ -35,7 +39,12 @@ protected InputStream createInputStream() {
if (exists()) {
return new FileInputStream(path);
} else {
return Resources.getResource(path).openStream();
try {
URL pathUrl = new URL(path);
return pathUrl.openStream();
} catch (MalformedURLException ignored) {
return Resources.getResource(path).openStream();
}
}
} catch (IOException e) {
return throwUnchecked(e, InputStream.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ public class KeyStoreSettings {

public static final KeyStoreSettings NO_STORE = new KeyStoreSettings(null, null, null);

private final AbstractKeyStoreSource keyStoreSource;
private final KeyStoreSource keyStoreSource;

public KeyStoreSettings(AbstractKeyStoreSource keyStoreSource) {
public KeyStoreSettings(KeyStoreSource keyStoreSource) {
this.keyStoreSource = keyStoreSource;
}

Expand All @@ -48,7 +48,7 @@ public String path() {
}

public String password() {
return new String(keyStoreSource.getKeyStorePassword());
return keyStoreSource.getKeyStorePassword();
}

public String type() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package com.github.tomakehurst.wiremock.common.ssl;

import com.github.tomakehurst.wiremock.common.Source;
import com.google.common.io.ByteStreams;
import org.apache.commons.codec.digest.DigestUtils;

import javax.xml.bind.DatatypeConverter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
Expand All @@ -10,12 +15,12 @@

import static com.github.tomakehurst.wiremock.common.Exceptions.throwUnchecked;

public abstract class AbstractKeyStoreSource implements Source<KeyStore> {
public abstract class KeyStoreSource implements Source<KeyStore> {

protected final String keyStoreType;
protected final char[] keyStorePassword;

protected AbstractKeyStoreSource(String keyStoreType, char[] keyStorePassword) {
protected KeyStoreSource(String keyStoreType, char[] keyStorePassword) {
this.keyStoreType = keyStoreType;
this.keyStorePassword = keyStorePassword;
}
Expand All @@ -41,21 +46,20 @@ public KeyStore load() {
}

protected abstract InputStream createInputStream();
public abstract boolean exists();

public String getKeyStoreType() {
return keyStoreType;
}

public char[] getKeyStorePassword() {
return keyStorePassword;
public String getKeyStorePassword() {
return new String(keyStorePassword);
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AbstractKeyStoreSource that = (AbstractKeyStoreSource) o;
KeyStoreSource that = (KeyStoreSource) o;
return keyStoreType.equals(that.keyStoreType) &&
Arrays.equals(keyStorePassword, that.keyStorePassword);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
package com.github.tomakehurst.wiremock.core;

import com.github.tomakehurst.wiremock.common.*;
import com.github.tomakehurst.wiremock.common.ssl.AbstractKeyStoreSource;
import com.github.tomakehurst.wiremock.common.ssl.FileOrClasspathKeyStoreSource;
import com.github.tomakehurst.wiremock.common.ssl.KeyStoreSettings;
import com.github.tomakehurst.wiremock.extension.Extension;
Expand Down Expand Up @@ -48,7 +47,6 @@
import static com.github.tomakehurst.wiremock.common.BrowserProxySettings.DEFAULT_CA_KEYSTORE_PATH;
import static com.github.tomakehurst.wiremock.core.WireMockApp.MAPPINGS_ROOT;
import static com.github.tomakehurst.wiremock.extension.ExtensionLoader.valueAssignableFrom;
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.collect.Lists.transform;
import static com.google.common.collect.Maps.newLinkedHashMap;
import static java.util.Arrays.asList;
Expand All @@ -65,6 +63,7 @@ public class WireMockConfiguration implements Options {
private int httpsPort = -1;
private String keyStorePath = Resources.getResource("keystore").toString();
private String keyStorePassword = "password";
private String keyManagerPassword = "password";
private String keyStoreType = "JKS";
private String trustStorePath;
private String trustStorePassword = "password";
Expand Down Expand Up @@ -189,6 +188,11 @@ public WireMockConfiguration keystorePassword(String keyStorePassword) {
return this;
}

public WireMockConfiguration keyManagerPassword(String keyManagerPassword) {
this.keyManagerPassword = keyManagerPassword;
return this;
}

public WireMockConfiguration keystoreType(String keyStoreType) {
this.keyStoreType = keyStoreType;
return this;
Expand Down Expand Up @@ -431,6 +435,7 @@ public HttpsSettings httpsSettings() {
.port(httpsPort)
.keyStorePath(keyStorePath)
.keyStorePassword(keyStorePassword)
.keyManagerPassword(keyManagerPassword)
.keyStoreType(keyStoreType)
.trustStorePath(trustStorePath)
.trustStorePassword(trustStorePassword)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,8 @@ protected ServerConnector createHttpsConnector(
SslContextFactory sslContextFactory = buildSslContextFactory();

sslContextFactory.setKeyStorePath(httpsSettings.keyStorePath());
sslContextFactory.setKeyManagerPassword(httpsSettings.keyStorePassword());
sslContextFactory.setKeyStorePassword(httpsSettings.keyStorePassword());
sslContextFactory.setKeyManagerPassword(httpsSettings.keyManagerPassword());
sslContextFactory.setKeyStoreType(httpsSettings.keyStoreType());
if (httpsSettings.hasTrustStore()) {
sslContextFactory.setTrustStorePath(httpsSettings.trustStorePath());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ public class CommandLineOptions implements Options {
private static final String HTTPS_PORT = "https-port";
private static final String HTTPS_KEYSTORE = "https-keystore";
private static final String HTTPS_KEYSTORE_PASSWORD = "keystore-password";
private static final String HTTPS_KEY_MANAGER_PASSWORD = "key-manager-password";
private static final String HTTPS_TRUSTSTORE = "https-truststore";
private static final String HTTPS_TRUSTSTORE_PASSWORD = "truststore-password";
private static final String REQUIRE_CLIENT_CERT = "https-require-client-cert";
Expand Down Expand Up @@ -127,6 +128,7 @@ public CommandLineOptions(String... args) {
optionParser.accepts(HTTPS_TRUSTSTORE_PASSWORD, "Password for the trust store").withRequiredArg();
optionParser.accepts(HTTPS_TRUSTSTORE, "Path to an alternative truststore for HTTPS client certificates. Must have a password of \"password\".").requiredIf(REQUIRE_CLIENT_CERT).withRequiredArg();
optionParser.accepts(HTTPS_KEYSTORE_PASSWORD, "Password for the alternative keystore.").withRequiredArg().defaultsTo("password");
optionParser.accepts(HTTPS_KEY_MANAGER_PASSWORD, "Key manager password for use with the alternative keystore.").withRequiredArg().defaultsTo("password");
optionParser.accepts(HTTPS_KEYSTORE, "Path to an alternative keystore for HTTPS. Password is assumed to be \"password\" if not specified.").requiredIf(HTTPS_TRUSTSTORE).requiredIf(HTTPS_KEYSTORE_PASSWORD).withRequiredArg().defaultsTo(Resources.getResource("keystore").toString());
optionParser.accepts(PROXY_ALL, "Will create a proxy mapping for /* to the specified URL").withRequiredArg();
optionParser.accepts(PRESERVE_HOST_HEADER, "Will transfer the original host header from the client to the proxied service");
Expand Down Expand Up @@ -279,6 +281,7 @@ public HttpsSettings httpsSettings() {
.port(httpsPortNumber())
.keyStorePath((String) optionSet.valueOf(HTTPS_KEYSTORE))
.keyStorePassword((String) optionSet.valueOf(HTTPS_KEYSTORE_PASSWORD))
.keyManagerPassword((String) optionSet.valueOf(HTTPS_KEY_MANAGER_PASSWORD))
.trustStorePath((String) optionSet.valueOf(HTTPS_TRUSTSTORE))
.trustStorePassword((String) optionSet.valueOf(HTTPS_TRUSTSTORE_PASSWORD))
.needClientAuth(optionSet.has(REQUIRE_CLIENT_CERT)).build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package com.github.tomakehurst.wiremock;

import com.github.tomakehurst.wiremock.client.WireMock;
import com.github.tomakehurst.wiremock.common.FatalStartupException;
import com.github.tomakehurst.wiremock.core.Options;
import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
import com.github.tomakehurst.wiremock.http.Fault;
Expand Down Expand Up @@ -175,13 +176,33 @@ public void acceptsAlternativeKeystore() throws Exception {

@Test
public void acceptsAlternativeKeystoreWithNonDefaultPassword() throws Exception {
String keystorePath = Resources.getResource("test-keystore-pwd").toString();
startServerWithKeystore(keystorePath, "anotherpassword");
String testKeystorePath = Resources.getResource("test-keystore-pwd").toString();
startServerWithKeystore(testKeystorePath, "nondefaultpass", "password");
stubFor(get(urlEqualTo("/https-test")).willReturn(aResponse().withStatus(200).withBody("HTTPS content")));

assertThat(contentFor(url("/https-test")), is("HTTPS content"));
}

@Test
public void acceptsAlternativeKeystoreWithNonDefaultKeyManagerPassword() throws Exception {
String keystorePath = Resources.getResource("test-keystore-key-man-pwd").toString();
startServerWithKeystore(keystorePath, "password", "anotherpassword");
stubFor(get(urlEqualTo("/alt-password-https")).willReturn(aResponse().withStatus(200).withBody("HTTPS content")));

assertThat(contentFor(url("/alt-password-https")), is("HTTPS content"));
}

@Test
public void failsToStartWithAlternativeKeystoreWithWrongKeyManagerPassword() {
try {
String keystorePath = Resources.getResource("test-keystore-key-man-pwd").toString();
startServerWithKeystore(keystorePath, "password", "wrongpassword");
fail("Expected a SocketException or SSLHandshakeException to be thrown");
} catch (Exception e) {
assertThat(e.getClass().getName(), is(FatalStartupException.class.getName()));
}
}

@Test
public void rejectsWithoutClientCertificate() {
startServerEnforcingClientCert(KEY_STORE_PATH, TRUST_STORE_PATH, TRUST_STORE_PASSWORD);
Expand Down Expand Up @@ -294,11 +315,12 @@ private void startServerEnforcingClientCert(String keystorePath, String truststo
httpClient = HttpClientFactory.createClient();
}

private void startServerWithKeystore(String keystorePath, String keystorePassword) {
private void startServerWithKeystore(String keystorePath, String keystorePassword, String keyManagerPassword) {
WireMockConfiguration config = wireMockConfig().dynamicPort().dynamicHttpsPort();
if (keystorePath != null) {
config.keystorePath(keystorePath);
config.keystorePassword(keystorePassword);
config.keystorePath(keystorePath)
.keystorePassword(keystorePassword)
.keyManagerPassword(keyManagerPassword);
}

wireMockServer = new WireMockServer(config);
Expand All @@ -309,7 +331,7 @@ private void startServerWithKeystore(String keystorePath, String keystorePasswor
}

private void startServerWithKeystore(String keystorePath) {
startServerWithKeystore(keystorePath, "password");
startServerWithKeystore(keystorePath, "password", "password");
}

private void startServerWithDefaultKeystore() {
Expand Down
Loading

0 comments on commit 6ded3d6

Please sign in to comment.