org.wildfly.security.ssl.SSLContextBuilder Maven / Gradle / Ivy
The newest version!
/*
* JBoss, Home of Professional Open Source.
* Copyright 2015 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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.wildfly.security.ssl;
import static org.wildfly.security.provider.util.ProviderUtil.INSTALLED_PROVIDERS;
import java.security.Provider;
import java.security.Security;
import java.util.function.Supplier;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSessionContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509KeyManager;
import javax.net.ssl.X509TrustManager;
import org.wildfly.common.Assert;
import org.wildfly.security.FixedSecurityFactory;
import org.wildfly.security.OneTimeSecurityFactory;
import org.wildfly.security.SecurityFactory;
import org.wildfly.security.auth.server.MechanismConfiguration;
import org.wildfly.security.auth.server.MechanismConfigurationSelector;
import org.wildfly.security.auth.server.SecurityDomain;
import org.wildfly.security.auth.server.PrincipalDecoder;
import org.wildfly.security.auth.server.ServerAuthenticationContext;
import org.wildfly.security.evidence.X509PeerCertificateChainEvidence;
/**
* A class which allows building and configuration of a single client- or server-side SSL context. The builder requires, at a
* minimum, that a key manager be set; all other parameters have default values as follows:
*
* - The security domain defaults to being empty (no client authentication possible)
* - The principal decoder defaults to the {@linkplain PrincipalDecoder#DEFAULT default principal decoder}
* - The cipher suite selector defaults to {@link CipherSuiteSelector#openSslDefault()}
* - The protocol suite selector defaults to {@link ProtocolSelector#DEFAULT_SELECTOR}
* - The "require client authentication" flag defaults to {@code false}
* - The provider supplier defaults to {@link Security#getProviders() Security::getProviders}
*
*
* @author David M. Lloyd
*/
public final class SSLContextBuilder {
private SecurityDomain securityDomain;
// ELY-1917: This should be changed to CipherSuiteSelector.openSslCombinedDefault once we are ready to enable
// TLS 1.3 by default
private CipherSuiteSelector cipherSuiteSelector = CipherSuiteSelector.openSslDefault();
private ProtocolSelector protocolSelector = ProtocolSelector.DEFAULT_SELECTOR;
private boolean useCipherSuitesOrder = true;
private boolean wantClientAuth;
private boolean needClientAuth;
private boolean authenticationOptional;
private boolean clientMode;
private int sessionCacheSize;
private int sessionTimeout;
private SecurityFactory keyManagerSecurityFactory;
private SecurityFactory trustManagerSecurityFactory = SSLUtils.getDefaultX509TrustManagerSecurityFactory();
private Supplier providerSupplier = INSTALLED_PROVIDERS;
private String providerName;
private boolean wrap = true;
private MechanismConfigurationSelector mechanismConfigurationSelector;
/**
* Set the security domain to use to authenticate clients.
*
* @param securityDomain the security domain to use to authenticate clients, or {@code null} to disable client
* certificate authentication
*/
public SSLContextBuilder setSecurityDomain(final SecurityDomain securityDomain) {
if (securityDomain != null && securityDomain.getEvidenceVerifySupport(X509PeerCertificateChainEvidence.class).isNotSupported()) {
throw ElytronMessages.tls.securityDomainOfSSLContextDoesNotSupportX509();
}
this.securityDomain = securityDomain;
return this;
}
/**
* Set the cipher suite selector to use for this context.
*
* @param cipherSuiteSelector the cipher suite selector (not {@code null})
*/
public SSLContextBuilder setCipherSuiteSelector(final CipherSuiteSelector cipherSuiteSelector) {
Assert.checkNotNullParam("cipherSuiteSelector", cipherSuiteSelector);
this.cipherSuiteSelector = cipherSuiteSelector;
return this;
}
/**
* Set the protocol selector to use for this context.
*
* @param protocolSelector the protocol selector to use for this context (not {@code null})
*/
public SSLContextBuilder setProtocolSelector(final ProtocolSelector protocolSelector) {
Assert.checkNotNullParam("protocolSelector", protocolSelector);
this.protocolSelector = protocolSelector;
return this;
}
/**
* Sets whether the local cipher suites preference should be honored.
*
* @param useCipherSuitesOrder whether the local cipher suites preference should be honored.
*/
public SSLContextBuilder setUseCipherSuitesOrder(final boolean useCipherSuitesOrder) {
Assert.checkNotNullParam("useCipherSuitesOrder", useCipherSuitesOrder);
this.useCipherSuitesOrder = useCipherSuitesOrder;
return this;
}
/**
* Force the SSLContext created by this builder to want client authentication.
*
* The SSLContext returned by this builder will be configured to want client authentication if this value is set to true OR
* of a SecurityDomain is associated.
*
* @param wantClientAuth should the SSLContext be forced to want client authentication.
*/
public SSLContextBuilder setWantClientAuth(final boolean wantClientAuth) {
this.wantClientAuth = wantClientAuth;
return this;
}
/**
* Force the SSLContext created by this builder to need client authentication.
*
* The SSLContext returned by this builder will be configured to need client authentication if this value is set to true.
*
* @param needClientAuth should the SSLContext be forced to need client authentication.
*/
public SSLContextBuilder setNeedClientAuth(final boolean needClientAuth) {
this.needClientAuth = needClientAuth;
return this;
}
/**
* Where a SecurityDomain is associated with this Builder if the client presents a certificate an attempt will be made to
* obtain a SecurityIdentity by using the certificate for authentication, setting this flag to {@code true} allows for a
* failed authentication to be silently ignored.
*
* This setting does not bypass any certificate checking performed by the underlying TrustManager so failure there will still cause the connection attempt to be aborted.
*
* The reason this setting would be used would be to enable a fallback to another authentication mechanism after the connection is established.
*
* Note: Where this is no security domain associated there is no authentication step so this value will be ignored.
*
* @param authenticationOptional should the authentication step be allowed to silently fail.
*/
public SSLContextBuilder setAuthenticationOptional(final boolean authenticationOptional) {
this.authenticationOptional = authenticationOptional;
return this;
}
/**
* Sets the size of the cache used for storing SSLSession objects.
*
* @param sessionCacheSize the size of the cache used for storing SSLSession objects.
* @return The {@link SSLContextBuilder} to allow chaining of method calls.
*/
public SSLContextBuilder setSessionCacheSize(final int sessionCacheSize) {
this.sessionCacheSize = sessionCacheSize;
return this;
}
/**
* Sets the timeout limit for SSLSession objects.
*
* @param sessionTimeout the timeout limit for SSLSession objects.
* @return The {@link SSLContextBuilder} to allow chaining of method calls.
*/
public SSLContextBuilder setSessionTimeout(final int sessionTimeout) {
this.sessionTimeout = sessionTimeout;
return this;
}
/**
* Set the factory for the key manager which should be used to hold identities for this context.
*
* @param keyManagerSecurityFactory the security factory which produces the key manager (not {@code null})
*/
public SSLContextBuilder setKeyManagerSecurityFactory(final SecurityFactory keyManagerSecurityFactory) {
Assert.checkNotNullParam("keyManagerSecurityFactory", keyManagerSecurityFactory);
this.keyManagerSecurityFactory = keyManagerSecurityFactory;
return this;
}
/**
* Set the key manager which should be used to hold identities for this context.
*
* @param keyManager the security factory which produces the key manager (not {@code null})
*/
public SSLContextBuilder setKeyManager(final X509ExtendedKeyManager keyManager) {
Assert.checkNotNullParam("keyManager", keyManager);
this.keyManagerSecurityFactory = new FixedSecurityFactory<>(keyManager);
return this;
}
/**
* Set the factory for the trust manager which should be used for the initial trust decisions during connection.
*
* @param trustManagerSecurityFactory the factory for the trust manager which should be used for the initial trust decisions during connection (not {@code null}).
*/
public SSLContextBuilder setTrustManagerSecurityFactory(final SecurityFactory trustManagerSecurityFactory) {
this.trustManagerSecurityFactory = Assert.checkNotNullParam("trustManagerSecurityFactory", trustManagerSecurityFactory);
return this;
}
/**
* Set the trust manager which should be used to hold identities for this context.
*
* @param trustManager the trust manager which should be used to hold identities for this context (not {@code null}).
*/
public SSLContextBuilder setTrustManager(final X509TrustManager trustManager) {
Assert.checkNotNullParam("trustManager", trustManager);
this.trustManagerSecurityFactory = new FixedSecurityFactory<>(trustManager);
return this;
}
// todo: add a setter which simply accepts a single org.wildfly.security.ssl.X500CertificateChainPrivateCredential instance
/**
* Set the provider supplier.
*
* @param providerSupplier the provider supplier (not {@code null})
*/
public SSLContextBuilder setProviderSupplier(final Supplier providerSupplier) {
Assert.checkNotNullParam("providerSupplier", providerSupplier);
this.providerSupplier = providerSupplier;
return this;
}
/**
* Set the provider name.
*
* @param name the provider name (if {@code null} and provider is allowed)
* @return this builder
*/
public SSLContextBuilder setProviderName(final String name) {
this.providerName = name;
return this;
}
/**
* Set the client mode of the target SSL context.
*
* @param clientMode {@code true} to use client mode, {@code false} otherwise
* @return this builder
*/
public SSLContextBuilder setClientMode(final boolean clientMode) {
this.clientMode = clientMode;
return this;
}
/**
* Set if the configured SSL engine and sockets created using the SSL context should be wrapped to prevent modification to the configuration.
*
* Defaults to {@code true}.
*
* @param wrap should the engine or socket created by the SSL context be wrapped to prevent modification to the configuration.
* @return this builder
*/
public SSLContextBuilder setWrap(final boolean wrap) {
this.wrap = wrap;
return this;
}
/**
* Set selector of mechanism configuration for {@link ServerAuthenticationContext}, which will be used for SSL client authentication.
*
* @param mechanismConfigurationSelector mechanism configuration selector to be used by {@link ServerAuthenticationContext} in SSL authentication.
* @return this builder
*/
public SSLContextBuilder setMechanismConfigurationSelector(final MechanismConfigurationSelector mechanismConfigurationSelector) {
this.mechanismConfigurationSelector = mechanismConfigurationSelector;
return this;
}
/**
* Build a security factory for the new context. The factory will cache the constructed instance.
*
* @return the security factory
*/
public SecurityFactory build() {
final SecurityDomain securityDomain = this.securityDomain;
final CipherSuiteSelector cipherSuiteSelector = this.cipherSuiteSelector;
final ProtocolSelector protocolSelector = this.protocolSelector;
final SecurityFactory trustManagerSecurityFactory = this.trustManagerSecurityFactory;
final SecurityFactory keyManagerSecurityFactory = this.keyManagerSecurityFactory;
final Supplier providerSupplier = this.providerSupplier;
final boolean clientMode = this.clientMode;
final boolean authenticationOptional = this.authenticationOptional;
final int sessionCacheSize = this.sessionCacheSize;
final int sessionTimeout = this.sessionTimeout;
final boolean wantClientAuth = this.wantClientAuth;
final boolean needClientAuth = this.needClientAuth;
final boolean useCipherSuitesOrder = this.useCipherSuitesOrder;
final boolean wrap = this.wrap;
final MechanismConfigurationSelector mechanismConfigurationSelector = this.mechanismConfigurationSelector != null ?
this.mechanismConfigurationSelector :
MechanismConfigurationSelector.constantSelector(MechanismConfiguration.EMPTY);
return new OneTimeSecurityFactory<>(() -> {
final SecurityFactory sslContextFactory = SSLUtils.createSslContextFactory(protocolSelector, providerSupplier, providerName);
// construct the original context
final SSLContext sslContext = sslContextFactory.create();
final X509KeyManager x509KeyManager = keyManagerSecurityFactory == null ? null : keyManagerSecurityFactory.create();
final X509TrustManager x509TrustManager = trustManagerSecurityFactory.create();
final boolean canAuthPeers = securityDomain != null && securityDomain.getEvidenceVerifySupport(X509PeerCertificateChainEvidence.class).mayBeSupported();
if (ElytronMessages.tls.isTraceEnabled()) {
ElytronMessages.tls.tracef("SSLContext initialization:%n" +
" securityDomain = %s%n" +
" canAuthPeers = %s%n" +
" cipherSuiteSelector = %s%n" +
" protocolSelector = %s%n" +
" x509TrustManager = %s%n" +
" x509KeyManager = %s%n" +
" providerSupplier = %s%n" +
" clientMode = %s%n" +
" authenticationOptional = %s%n" +
" sessionCacheSize = %s%n" +
" sessionTimeout = %s%n" +
" wantClientAuth = %s%n" +
" needClientAuth = %s%n" +
" useCipherSuitesOrder = %s%n" +
" wrap = %s%n",
securityDomain, canAuthPeers, cipherSuiteSelector, protocolSelector, x509TrustManager,
x509KeyManager, providerSupplier, clientMode, authenticationOptional, sessionCacheSize,
sessionTimeout, wantClientAuth, needClientAuth, useCipherSuitesOrder, wrap);
}
sslContext.init(x509KeyManager == null ? null : new KeyManager[]{
x509KeyManager
}, new TrustManager[]{
canAuthPeers ?
new SecurityDomainTrustManager(x509TrustManager, securityDomain, authenticationOptional, mechanismConfigurationSelector) :
x509TrustManager
}, null);
SSLSessionContext sessionContext = clientMode ? sslContext.getClientSessionContext() : sslContext.getServerSessionContext();
if (sessionContext != null) {
if (sessionCacheSize >= 0) sessionContext.setSessionCacheSize(sessionCacheSize);
if (sessionTimeout >= 0) sessionContext.setSessionTimeout(sessionTimeout);
}
// now, set up the wrapping configuration
final SSLConfigurator sslConfigurator = clientMode ?
new SSLConfiguratorImpl(protocolSelector, cipherSuiteSelector, useCipherSuitesOrder) :
new SSLConfiguratorImpl(protocolSelector, cipherSuiteSelector, wantClientAuth || canAuthPeers, needClientAuth, useCipherSuitesOrder);
final ConfiguredSSLContextSpi contextSpi = new ConfiguredSSLContextSpi(sslContext, sslConfigurator, wrap);
return new DelegatingSSLContext(contextSpi);
});
}
}