
io.dropwizard.jetty.HttpsConnectorFactory Maven / Gradle / Ivy
The newest version!
package io.dropwizard.jetty;
import com.codahale.metrics.MetricRegistry;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeName;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
import io.dropwizard.validation.ValidationMethod;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.SecureRequestCustomizer;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
import org.eclipse.jetty.util.thread.Scheduler;
import org.eclipse.jetty.util.thread.ThreadPool;
import org.hibernate.validator.constraints.NotEmpty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import java.io.File;
import java.net.URI;
import java.security.KeyStore;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Builds HTTPS connectors (HTTP over TLS/SSL).
*
* Configuration Parameters:
*
*
* Name
* Default
* Description
*
*
* {@code keyStorePath}
* REQUIRED
*
* The path to the Java key store which contains the host certificate and private key.
*
*
*
* {@code keyStorePassword}
* REQUIRED
*
* The password used to access the key store.
*
*
*
* {@code keyStoreType}
* {@code JKS}
*
* The type of key store (usually {@code JKS}, {@code PKCS12}, {@code JCEKS},
* {@code Windows-MY}, or {@code Windows-ROOT}).
*
*
*
* {@code keyStoreProvider}
* (none)
*
* The JCE provider to use to access the key store.
*
*
*
* {@code trustStorePath}
* (none)
*
* The path to the Java key store which contains the CA certificates used to establish
* trust.
*
*
*
* {@code trustStorePassword}
* (none)
* The password used to access the trust store.
*
*
* {@code trustStoreType}
* {@code JKS}
*
* The type of trust store (usually {@code JKS}, {@code PKCS12}, {@code JCEKS},
* {@code Windows-MY}, or {@code Windows-ROOT}).
*
*
*
* {@code trustStoreProvider}
* (none)
*
* The JCE provider to use to access the trust store.
*
*
*
* {@code keyManagerPassword}
* (none)
* The password, if any, for the key manager.
*
*
* {@code needClientAuth}
* (none)
* Whether or not client authentication is required.
*
*
* {@code wantClientAuth}
* (none)
* Whether or not client authentication is requested.
*
*
* {@code certAlias}
* (none)
* The alias of the certificate to use.
*
*
* {@code crlPath}
* (none)
* The path to the file which contains the Certificate Revocation List.
*
*
* {@code enableCRLDP}
* false
* Whether or not CRL Distribution Points (CRLDP) support is enabled.
*
*
* {@code enableOCSP}
* false
* Whether or not On-Line Certificate Status Protocol (OCSP) support is enabled.
*
*
* {@code maxCertPathLength}
* (unlimited)
* The maximum certification path length.
*
*
* {@code ocspResponderUrl}
* (none)
* The location of the OCSP responder.
*
*
* {@code jceProvider}
* (none)
* The name of the JCE provider to use for cryptographic support.
*
*
* {@code validateCerts}
* false
*
* Whether or not to validate TLS certificates before starting. If enabled, Dropwizard
* will refuse to start with expired or otherwise invalid certificates. This option will
* cause unconditional failure in Dropwizard 1.x until a new validation mechanism can be
* implemented.
*
*
*
* {@code validatePeers}
* false
*
* Whether or not to validate TLS peer certificates. This option will
* cause unconditional failure in Dropwizard 1.x until a new validation mechanism can be
* implemented.
*
*
*
* {@code supportedProtocols}
* JVM default
*
* A list of protocols (e.g., {@code SSLv3}, {@code TLSv1}) which are supported. All
* other protocols will be refused.
*
*
*
* {@code excludedProtocols}
* Jetty's default
*
* A list of protocols (e.g., {@code SSLv3}, {@code TLSv1}) which are excluded. These
* protocols will be refused.
*
*
*
* {@code supportedCipherSuites}
* JVM default
*
* A list of cipher suites (e.g., {@code TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256}) which
* are supported. All other cipher suites will be refused
*
*
*
* {@code excludedCipherSuites}
* Jetty's default
*
* A list of cipher suites (e.g., {@code TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256}) which
* are excluded. These cipher suites will be refused.
*
*
*
* {@code allowRenegotiation}
* true
* Whether or not TLS renegotiation is allowed.
*
*
* {@code endpointIdentificationAlgorithm}
* (none)
*
* Which endpoint identification algorithm, if any, to use during the TLS handshake.
*
*
*
*
* For more configuration parameters, see {@link HttpConnectorFactory}.
*
* @see HttpConnectorFactory
*/
@JsonTypeName("https")
public class HttpsConnectorFactory extends HttpConnectorFactory {
private static final Logger LOGGER = LoggerFactory.getLogger(HttpsConnectorFactory.class);
private static final AtomicBoolean LOGGED = new AtomicBoolean(false);
@Nullable
private String keyStorePath;
@Nullable
private String keyStorePassword;
@NotEmpty
private String keyStoreType = "JKS";
@Nullable
private String keyStoreProvider;
@Nullable
private String trustStorePath;
@Nullable
private String trustStorePassword;
@NotEmpty
private String trustStoreType = "JKS";
@Nullable
private String trustStoreProvider;
@Nullable
private String keyManagerPassword;
@Nullable
private Boolean needClientAuth;
@Nullable
private Boolean wantClientAuth;
@Nullable
private String certAlias;
@Nullable
private File crlPath;
@Nullable
private Boolean enableCRLDP;
@Nullable
private Boolean enableOCSP;
@Nullable
private Integer maxCertPathLength;
@Nullable
private URI ocspResponderUrl;
@Nullable
private String jceProvider;
private boolean validateCerts = false;
private boolean validatePeers = false;
@Nullable
private List supportedProtocols;
@Nullable
private List excludedProtocols;
@Nullable
private List supportedCipherSuites;
@Nullable
private List excludedCipherSuites;
private boolean allowRenegotiation = true;
@Nullable
private String endpointIdentificationAlgorithm;
@JsonProperty
public boolean getAllowRenegotiation() {
return allowRenegotiation;
}
@JsonProperty
public void setAllowRenegotiation(boolean allowRenegotiation) {
this.allowRenegotiation = allowRenegotiation;
}
@JsonProperty
@Nullable
public String getEndpointIdentificationAlgorithm() {
return endpointIdentificationAlgorithm;
}
@JsonProperty
public void setEndpointIdentificationAlgorithm(String endpointIdentificationAlgorithm) {
this.endpointIdentificationAlgorithm = endpointIdentificationAlgorithm;
}
@JsonProperty
@Nullable
public String getKeyStorePath() {
return keyStorePath;
}
@JsonProperty
public void setKeyStorePath(String keyStorePath) {
this.keyStorePath = keyStorePath;
}
@JsonProperty
@Nullable
public String getKeyStorePassword() {
return keyStorePassword;
}
@JsonProperty
public void setKeyStorePassword(String keyStorePassword) {
this.keyStorePassword = keyStorePassword;
}
@JsonProperty
public String getKeyStoreType() {
return keyStoreType;
}
@JsonProperty
public void setKeyStoreType(String keyStoreType) {
this.keyStoreType = keyStoreType;
}
@JsonProperty
@Nullable
public String getKeyStoreProvider() {
return keyStoreProvider;
}
@JsonProperty
public void setKeyStoreProvider(String keyStoreProvider) {
this.keyStoreProvider = keyStoreProvider;
}
@JsonProperty
public String getTrustStoreType() {
return trustStoreType;
}
@JsonProperty
public void setTrustStoreType(String trustStoreType) {
this.trustStoreType = trustStoreType;
}
@JsonProperty
@Nullable
public String getTrustStoreProvider() {
return trustStoreProvider;
}
@JsonProperty
public void setTrustStoreProvider(String trustStoreProvider) {
this.trustStoreProvider = trustStoreProvider;
}
@JsonProperty
@Nullable
public String getKeyManagerPassword() {
return keyManagerPassword;
}
@JsonProperty
public void setKeyManagerPassword(String keyManagerPassword) {
this.keyManagerPassword = keyManagerPassword;
}
@JsonProperty
@Nullable
public String getTrustStorePath() {
return trustStorePath;
}
@JsonProperty
public void setTrustStorePath(String trustStorePath) {
this.trustStorePath = trustStorePath;
}
@JsonProperty
@Nullable
public String getTrustStorePassword() {
return trustStorePassword;
}
@JsonProperty
public void setTrustStorePassword(@Nullable String trustStorePassword) {
this.trustStorePassword = trustStorePassword;
}
@JsonProperty
@Nullable
public Boolean getNeedClientAuth() {
return needClientAuth;
}
@JsonProperty
public void setNeedClientAuth(Boolean needClientAuth) {
this.needClientAuth = needClientAuth;
}
@JsonProperty
@Nullable
public Boolean getWantClientAuth() {
return wantClientAuth;
}
@JsonProperty
public void setWantClientAuth(Boolean wantClientAuth) {
this.wantClientAuth = wantClientAuth;
}
@JsonProperty
@Nullable
public String getCertAlias() {
return certAlias;
}
@JsonProperty
public void setCertAlias(String certAlias) {
this.certAlias = certAlias;
}
@JsonProperty
@Nullable
public File getCrlPath() {
return crlPath;
}
@JsonProperty
public void setCrlPath(File crlPath) {
this.crlPath = crlPath;
}
@JsonProperty
@Nullable
public Boolean getEnableCRLDP() {
return enableCRLDP;
}
@JsonProperty
public void setEnableCRLDP(Boolean enableCRLDP) {
this.enableCRLDP = enableCRLDP;
}
@JsonProperty
@Nullable
public Boolean getEnableOCSP() {
return enableOCSP;
}
@JsonProperty
public void setEnableOCSP(Boolean enableOCSP) {
this.enableOCSP = enableOCSP;
}
@JsonProperty
@Nullable
public Integer getMaxCertPathLength() {
return maxCertPathLength;
}
@JsonProperty
public void setMaxCertPathLength(Integer maxCertPathLength) {
this.maxCertPathLength = maxCertPathLength;
}
@JsonProperty
@Nullable
public URI getOcspResponderUrl() {
return ocspResponderUrl;
}
@JsonProperty
public void setOcspResponderUrl(URI ocspResponderUrl) {
this.ocspResponderUrl = ocspResponderUrl;
}
@JsonProperty
@Nullable
public String getJceProvider() {
return jceProvider;
}
@JsonProperty
public void setJceProvider(String jceProvider) {
this.jceProvider = jceProvider;
}
@JsonProperty
public boolean getValidatePeers() {
return validatePeers;
}
@JsonProperty
public void setValidatePeers(boolean validatePeers) {
this.validatePeers = validatePeers;
}
@JsonProperty
@Nullable
public List getSupportedProtocols() {
return supportedProtocols;
}
@JsonProperty
public void setSupportedProtocols(List supportedProtocols) {
this.supportedProtocols = supportedProtocols;
}
@JsonProperty
@Nullable
public List getExcludedProtocols() {
return excludedProtocols;
}
@JsonProperty
public void setExcludedProtocols(List excludedProtocols) {
this.excludedProtocols = excludedProtocols;
}
@JsonProperty
@Nullable
public List getSupportedCipherSuites() {
return supportedCipherSuites;
}
@JsonProperty
@Nullable
public List getExcludedCipherSuites() {
return excludedCipherSuites;
}
@JsonProperty
public void setExcludedCipherSuites(List excludedCipherSuites) {
this.excludedCipherSuites = excludedCipherSuites;
}
@JsonProperty
public void setSupportedCipherSuites(List supportedCipherSuites) {
this.supportedCipherSuites = supportedCipherSuites;
}
@JsonProperty
public boolean isValidateCerts() {
return validateCerts;
}
@JsonProperty
public void setValidateCerts(boolean validateCerts) {
this.validateCerts = validateCerts;
}
@ValidationMethod(message = "keyStorePath should not be null")
public boolean isValidKeyStorePath() {
return keyStoreType.startsWith("Windows-") || keyStorePath != null;
}
@ValidationMethod(message = "keyStorePassword should not be null or empty")
public boolean isValidKeyStorePassword() {
return keyStoreType.startsWith("Windows-") ||
!Strings.isNullOrEmpty(keyStorePassword);
}
@Override
public Connector build(Server server, MetricRegistry metrics, String name, @Nullable ThreadPool threadPool) {
final HttpConfiguration httpConfig = buildHttpConfiguration();
final HttpConnectionFactory httpConnectionFactory = buildHttpConnectionFactory(httpConfig);
final SslContextFactory sslContextFactory = configureSslContextFactory(new SslContextFactory());
sslContextFactory.addLifeCycleListener(logSslInfoOnStart(sslContextFactory));
server.addBean(sslContextFactory);
server.addBean(new SslReload(sslContextFactory, this::configureSslContextFactory));
final SslConnectionFactory sslConnectionFactory =
new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.toString());
final Scheduler scheduler = new ScheduledExecutorScheduler();
final ByteBufferPool bufferPool = buildBufferPool();
return buildConnector(server, scheduler, bufferPool, name, threadPool,
new Jetty93InstrumentedConnectionFactory(
sslConnectionFactory,
metrics.timer(httpConnections())),
httpConnectionFactory);
}
@Override
protected HttpConfiguration buildHttpConfiguration() {
final HttpConfiguration config = super.buildHttpConfiguration();
config.setSecureScheme("https");
config.setSecurePort(getPort());
config.addCustomizer(new SecureRequestCustomizer());
return config;
}
/** Register a listener that waits until the ssl context factory has started. Once it has
* started we can grab the fully initialized context so we can log the parameters.
*/
protected AbstractLifeCycle.AbstractLifeCycleListener logSslInfoOnStart(final SslContextFactory sslContextFactory) {
return new AbstractLifeCycle.AbstractLifeCycleListener() {
@Override
public void lifeCycleStarted(LifeCycle event) {
logSupportedParameters(sslContextFactory.getSslContext());
}
};
}
private void logSupportedParameters(SSLContext context) {
if (LOGGED.compareAndSet(false, true)) {
final String[] protocols = context.getSupportedSSLParameters().getProtocols();
final SSLSocketFactory factory = context.getSocketFactory();
final String[] cipherSuites = factory.getSupportedCipherSuites();
LOGGER.info("Supported protocols: {}", Arrays.toString(protocols));
LOGGER.info("Supported cipher suites: {}", Arrays.toString(cipherSuites));
if (getSupportedProtocols() != null) {
LOGGER.info("Configured protocols: {}", getSupportedProtocols());
}
if (getExcludedProtocols() != null) {
LOGGER.info("Excluded protocols: {}", getExcludedProtocols());
}
if (getSupportedCipherSuites() != null) {
LOGGER.info("Configured cipher suites: {}", getSupportedCipherSuites());
}
if (getExcludedCipherSuites() != null) {
LOGGER.info("Excluded cipher suites: {}", getExcludedCipherSuites());
}
}
}
protected SslContextFactory configureSslContextFactory(SslContextFactory factory) {
if (keyStorePath != null) {
factory.setKeyStorePath(keyStorePath);
}
final String keyStoreType = getKeyStoreType();
if (keyStoreType.startsWith("Windows-")) {
try {
final KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
factory.setKeyStore(keyStore);
} catch (Exception e) {
throw new IllegalStateException("Windows key store not supported", e);
}
} else {
factory.setKeyStoreType(keyStoreType);
factory.setKeyStorePassword(keyStorePassword);
}
if (keyStoreProvider != null) {
factory.setKeyStoreProvider(keyStoreProvider);
}
final String trustStoreType = getTrustStoreType();
if (trustStoreType.startsWith("Windows-")) {
try {
final KeyStore keyStore = KeyStore.getInstance(trustStoreType);
keyStore.load(null, null);
factory.setTrustStore(keyStore);
} catch (Exception e) {
throw new IllegalStateException("Windows key store not supported", e);
}
} else {
if (trustStorePath != null) {
factory.setTrustStorePath(trustStorePath);
}
if (trustStorePassword != null) {
factory.setTrustStorePassword(trustStorePassword);
}
factory.setTrustStoreType(trustStoreType);
}
if (trustStoreProvider != null) {
factory.setTrustStoreProvider(trustStoreProvider);
}
if (keyManagerPassword != null) {
factory.setKeyManagerPassword(keyManagerPassword);
}
if (needClientAuth != null) {
factory.setNeedClientAuth(needClientAuth);
}
if (wantClientAuth != null) {
factory.setWantClientAuth(wantClientAuth);
}
if (certAlias != null) {
factory.setCertAlias(certAlias);
}
if (crlPath != null) {
factory.setCrlPath(crlPath.getAbsolutePath());
}
if (enableCRLDP != null) {
factory.setEnableCRLDP(enableCRLDP);
}
if (enableOCSP != null) {
factory.setEnableOCSP(enableOCSP);
}
if (maxCertPathLength != null) {
factory.setMaxCertPathLength(maxCertPathLength);
}
if (ocspResponderUrl != null) {
factory.setOcspResponderURL(ocspResponderUrl.toASCIIString());
}
if (jceProvider != null) {
factory.setProvider(jceProvider);
}
factory.setRenegotiationAllowed(allowRenegotiation);
factory.setEndpointIdentificationAlgorithm(endpointIdentificationAlgorithm);
factory.setValidateCerts(validateCerts);
factory.setValidatePeerCerts(validatePeers);
if (supportedProtocols != null) {
factory.setIncludeProtocols(Iterables.toArray(supportedProtocols, String.class));
}
if (excludedProtocols != null) {
factory.setExcludeProtocols(Iterables.toArray(excludedProtocols, String.class));
}
if (supportedCipherSuites != null) {
factory.setIncludeCipherSuites(Iterables.toArray(supportedCipherSuites, String.class));
}
if (excludedCipherSuites != null) {
factory.setExcludeCipherSuites(Iterables.toArray(excludedCipherSuites, String.class));
}
return factory;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy