Skip to content

Commit

Permalink
WFCORE-6802 [Preview] OCSP stapling support
Browse files Browse the repository at this point in the history
  • Loading branch information
Prarthona Paul committed May 15, 2024
1 parent 056fbe0 commit 4cad292
Show file tree
Hide file tree
Showing 9 changed files with 137 additions and 37 deletions.
1 change: 1 addition & 0 deletions elytron/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,7 @@
<exclude>jacc-with-providers.xml</exclude>
<exclude>legacy*.xml</exclude>
<exclude>elytron-subsystem-community*.xml</exclude>
<exclude>elytron-subsystem-preview*.xml</exclude>
</excludes>
<systemId>src/main/resources/schema/wildfly-elytron_18_0.xsd</systemId>
</validationSet>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,7 @@ interface ElytronDescriptionConstants {
String OBTAIN_KERBEROS_TICKET = "obtain-kerberos-ticket";
String OCSP = "ocsp";
String OCSP_STAPLING = "ocsp-stapling";
String OCSP_STAPLING_SOFT_FAIL = "ocsp-stapling-soft-fail";
String OID = "oid";
String ONLY_LEAF_CERT = "only-leaf-cert";
String OPERATIONS = "operations";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
//import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.Socket;
Expand Down Expand Up @@ -238,6 +239,12 @@ class SSLDefinitions {
.setDefaultValue(ModelNode.FALSE)
.build();

static final SimpleAttributeDefinition OCSP_STAPLING_SOFT_FAIL = new SimpleAttributeDefinitionBuilder(ElytronDescriptionConstants.OCSP_STAPLING_SOFT_FAIL, ModelType.BOOLEAN, true)
.setAllowExpression(true)
.setRestartAllServices()
.setDefaultValue(ModelNode.TRUE)
.build();

private static final String[] ALLOWED_PROTOCOLS = { "SSLv2", "SSLv2Hello", "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3" };

static final StringListAttributeDefinition PROTOCOLS = new StringListAttributeDefinition.Builder(ElytronDescriptionConstants.PROTOCOLS)
Expand Down Expand Up @@ -996,31 +1003,6 @@ private File resolveFileLocation(String path, String relativeTo, InjectedValue<P
}
return resolvedPath;
}

private TrustManagerFactory createTrustManagerFactory(Provider[] providers, String providerName, String algorithm) throws StartException {
TrustManagerFactory trustManagerFactory = null;

if (providers != null) {
for (Provider current : providers) {
if (providerName == null || providerName.equals(current.getName())) {
try {
// TODO - We could check the Services within each Provider to check there is one of the required type/algorithm
// However the same loop would need to remain as it is still possible a specific provider can't create it.
return TrustManagerFactory.getInstance(algorithm, current);
} catch (NoSuchAlgorithmException ignored) {
}
}
}
if (trustManagerFactory == null)
throw ROOT_LOGGER.unableToCreateManagerFactory(TrustManagerFactory.class.getSimpleName(), algorithm);
}

try {
return TrustManagerFactory.getInstance(algorithm);
} catch (NoSuchAlgorithmException e) {
throw new StartException(e);
}
}
};

ResourceDescriptionResolver resolver = ElytronExtension.getResourceDescriptionResolver(ElytronDescriptionConstants.TRUST_MANAGER);
Expand Down Expand Up @@ -1528,7 +1510,7 @@ static ResourceDefinition getClientSSLContextDefinition(boolean serverOrHostCont
.build();

AttributeDefinition[] attributes = new AttributeDefinition[]{CIPHER_SUITE_FILTER, CIPHER_SUITE_NAMES, PROTOCOLS,
KEY_MANAGER, TRUST_MANAGER, providersDefinition, PROVIDER_NAME, ACCEPT_OCSP_STAPLING};
KEY_MANAGER, TRUST_MANAGER, providersDefinition, PROVIDER_NAME, ACCEPT_OCSP_STAPLING, OCSP_STAPLING_SOFT_FAIL};

AbstractAddStepHandler add = new TrivialAddHandler<SSLContext>(SSLContext.class, attributes, SSL_CONTEXT_RUNTIME_CAPABILITY) {
@Override
Expand All @@ -1543,15 +1525,29 @@ protected ValueSupplier<SSLContext> getValueSupplier(ServiceBuilder<SSLContext>
final String cipherSuiteFilter = CIPHER_SUITE_FILTER.resolveModelAttribute(context, model).asString(); // has default value, can't be null
final String cipherSuiteNames = CIPHER_SUITE_NAMES.resolveModelAttribute(context, model).asStringOrNull(); // doesn't have a default value yet since we are disabling TLS 1.3 by default
final boolean acceptOCSPStapling = ACCEPT_OCSP_STAPLING.resolveModelAttribute(context, model).asBoolean();
final boolean softFail = OCSP_STAPLING_SOFT_FAIL.resolveModelAttribute(context, model).asBoolean();
return () -> {
X509ExtendedKeyManager keyManager = getX509KeyManager(keyManagerInjector.getOptionalValue());
X509ExtendedTrustManager trustManager = getX509TrustManager(trustManagerInjector.getOptionalValue());
Provider[] providers = filterProviders(providersInjector.getOptionalValue(), providerName);

SSLContextBuilder builder = new SSLContextBuilder();
if (keyManager != null) builder.setKeyManager(keyManager);
if (acceptOCSPStapling) {
TrustManagerFactory trustManagerFactory = createTrustManagerFactory(providersInjector.getOptionalValue(), providerName, TrustManagerFactory.getDefaultAlgorithm());
X509RevocationTrustManager.Builder revocationBuilder = X509RevocationTrustManager.builder();
// TODO: determine if the following approach is valid
revocationBuilder.setTrustManagerFactory(trustManagerFactory);
revocationBuilder.setTrustStore(getKeyStoreFromTrustManager(trustManager));

revocationBuilder.setCheckRevocation(true);
revocationBuilder.setSoftFail(softFail);
trustManager = revocationBuilder.build();
}

if (trustManager != null) builder.setTrustManager(trustManager);
if (providers != null) builder.setProviderSupplier(() -> providers);

builder.setCipherSuiteSelector(CipherSuiteSelector.aggregate(cipherSuiteNames != null ? CipherSuiteSelector.fromNamesString(cipherSuiteNames) : null, CipherSuiteSelector.fromString(cipherSuiteFilter)));
if (!protocols.isEmpty()) {
List<Protocol> list = new ArrayList<>();
Expand Down Expand Up @@ -1758,4 +1754,40 @@ public InjectedValue<PathManager> getPathManagerInjector() {
}
}

private static TrustManagerFactory createTrustManagerFactory(Provider[] providers, String providerName, String algorithm) throws StartException {
TrustManagerFactory trustManagerFactory = null;

if (providers != null) {
for (Provider current : providers) {
if (providerName == null || providerName.equals(current.getName())) {
try {
// TODO - We could check the Services within each Provider to check there is one of the required type/algorithm
// However the same loop would need to remain as it is still possible a specific provider can't create it.
return TrustManagerFactory.getInstance(algorithm, current);
} catch (NoSuchAlgorithmException ignored) {
}
}
}
if (trustManagerFactory == null)
throw ROOT_LOGGER.unableToCreateManagerFactory(TrustManagerFactory.class.getSimpleName(), algorithm);
}

try {
return TrustManagerFactory.getInstance(algorithm);
} catch (NoSuchAlgorithmException e) {
throw new StartException(e);
}
}

public static KeyStore getKeyStoreFromTrustManager(X509ExtendedTrustManager trustManager) throws Exception {
// TODO: proporly extract the keystore from the trustmanager
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null);
X509Certificate[] trustedCerts = trustManager.getAcceptedIssuers();
for (X509Certificate certificate : trustedCerts) {
trustStore.setCertificateEntry(certificate.getSerialNumber().toString(), certificate);
}
return trustStore;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ class TlsParser {
.addAttribute(SSLDefinitions.POST_REALM_PRINCIPAL_TRANSFORMER)
.addAttribute(SSLDefinitions.FINAL_PRINCIPAL_TRANSFORMER)
.addAttribute(SSLDefinitions.REALM_MAPPER)
.addAttribute(SSLDefinitions.OCSP_STAPLING); // new
.addAttribute(SSLDefinitions.OCSP_STAPLING); // new OCSP_STAPLING element

private PersistentResourceXMLBuilder clientSslContextParser = PersistentResourceXMLDescription.builder(PathElement.pathElement(CLIENT_SSL_CONTEXT))
.setXmlWrapperElement(CLIENT_SSL_CONTEXTS)
Expand Down Expand Up @@ -248,7 +248,7 @@ class TlsParser {
.addAttribute(SSLDefinitions.PROVIDERS)
.addAttribute(SSLDefinitions.PROVIDER_NAME);

private PersistentResourceXMLBuilder clientSslContextParserCommunity_18_0 = PersistentResourceXMLDescription.builder(PathElement.pathElement(CLIENT_SSL_CONTEXT))
private PersistentResourceXMLBuilder clientSslContextParserPreview_18_0 = PersistentResourceXMLDescription.builder(PathElement.pathElement(CLIENT_SSL_CONTEXT))
.setXmlWrapperElement(CLIENT_SSL_CONTEXTS)
.addAttribute(SSLDefinitions.SECURITY_DOMAIN)
.addAttribute(SSLDefinitions.CIPHER_SUITE_FILTER)
Expand All @@ -265,7 +265,8 @@ class TlsParser {
.addAttribute(SSLDefinitions.TRUST_MANAGER)
.addAttribute(SSLDefinitions.PROVIDERS)
.addAttribute(SSLDefinitions.PROVIDER_NAME)
.addAttribute(SSLDefinitions.ACCEPT_OCSP_STAPLING); //new
.addAttribute(SSLDefinitions.ACCEPT_OCSP_STAPLING) //new
.addAttribute(SSLDefinitions.OCSP_STAPLING_SOFT_FAIL); //new

private PersistentResourceXMLBuilder certificateAuthorityAccountParser = PersistentResourceXMLDescription.builder(PathElement.pathElement(CERTIFICATE_AUTHORITY_ACCOUNT))
.setXmlWrapperElement(CERTIFICATE_AUTHORITY_ACCOUNTS)
Expand Down Expand Up @@ -423,8 +424,8 @@ public void marshallSingleElement(AttributeDefinition attribute, ModelNode mappi
)
.addChild(keyManagerParser_12_0)
.addChild(trustManagerParser_14_0)
.addChild(serverSslContextPreviewParser_18_0) // new
.addChild(clientSslContextParserCommunity_18_0) // new
.addChild(serverSslContextPreviewParser_18_0) // new parser with ocsp_stapling
.addChild(clientSslContextParserPreview_18_0) // new parser with ocsp_stapling
.addChild(certificateAuthorityParser)
.addChild(certificateAuthorityAccountParser)
.addChild(serverSslSniContextParser)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,11 @@ void setValueSupplier(ValueSupplier<T> valueSupplier) {

@Override
public void start(StartContext context) throws StartException {
value = checkNotNullParam("valueSupplier", valueSupplier).get();
try {
value = checkNotNullParam("valueSupplier", valueSupplier).get();
} catch (Exception e) {
throw new RuntimeException(e);
}
if (valueConsumer != null) {
valueConsumer.accept(value);
}
Expand All @@ -69,7 +73,7 @@ public T getValue() throws IllegalStateException, IllegalArgumentException {
@FunctionalInterface
interface ValueSupplier<T> {

T get() throws StartException;
T get() throws Exception;

default void dispose() {}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1387,6 +1387,7 @@ elytron.client-ssl-context.trust-manager=Reference to the trust manager to use w
elytron.client-ssl-context.provider-name=The name of the provider to use. If not specified, all providers from providers will be passed to the SSLContext.
elytron.client-ssl-context.providers=The name of the providers to obtain the Provider[] to use to load the SSLContext.
elytron.client-ssl-context.accept-ocsp-stapling=Indicates whether the client would accept OCSP stapled responses fom the model or not.
elytron.client-ssl-context.ocsp-stapling-soft-fail=Determines client behaviour upon receiving an unknown OCSP-stapled response from the server.
# Runtime Attributes
elytron.client-ssl-context.active-session-count=The count of current active sessions.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5437,6 +5437,13 @@
</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="ocsp-stapling-soft-fail" type="xs:boolean" use="optional" default="true">
<xs:annotation>
<xs:documentation>
Indicates the behaviour of the client when the stapled status of the server's certificate is unknown.
</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>

<xs:complexType name="keyStoresType">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
Expand Down Expand Up @@ -59,6 +60,7 @@
import org.jboss.as.controller.security.CredentialReference;
import org.jboss.as.subsystem.test.AbstractSubsystemTest;
import org.jboss.as.subsystem.test.KernelServices;
import org.jboss.as.version.Stability;
import org.jboss.dmr.ModelNode;
import org.jboss.msc.service.ServiceController;
import org.jboss.msc.service.ServiceName;
Expand Down Expand Up @@ -109,13 +111,15 @@ public class TlsTestCase extends AbstractSubsystemTest {
private static final String NEGOTIATED_PROTOCOL = "negotiatedProtocol";

private static final String INIT_TEST_FILE = "/trust-manager-reload-test.truststore";
private static final String INIT_TEST_SERVER_SSL_CONTEXT = "serverContext";
private static final String INIT_TEST_CLIENT_SSL_CONTEXT = "clientContext";
private static final String INIT_TEST_TRUSTSTORE = "myTS";
private static final String INIT_TEST_TRUSTMANAGER = "myTM";
public static String disabledAlgorithms;


public TlsTestCase() {
super(ElytronExtension.SUBSYSTEM_NAME, new ElytronExtension());
super(ElytronExtension.SUBSYSTEM_NAME, new ElytronExtension(), Stability.PREVIEW);
}

private KernelServices services = null;
Expand Down Expand Up @@ -328,7 +332,7 @@ public void prepare() throws Throwable {
if (services != null) return;
String subsystemXml;
subsystemXml = JdkUtils.getJavaSpecVersion() <= 12 ? "tls-sun.xml" : "tls-oracle13plus.xml";
services = super.createKernelServicesBuilder(new TestEnvironment()).setSubsystemXmlResource(subsystemXml).build();
services = super.createKernelServicesBuilder(new TestEnvironment(Stability.PREVIEW)).setSubsystemXmlResource(subsystemXml).build();
if (!services.isSuccessfulBoot()) {
if (services.getBootError() != null) {
Assert.fail(services.getBootError().toString());
Expand Down Expand Up @@ -644,7 +648,54 @@ public void testOcspSimple() {
MatcherAssert.assertThat(trustManager, CoreMatchers.instanceOf(X509RevocationTrustManager.class));
}

private SSLContext getSslContext(String contextName) {
@Test
public void testOcspStaplingServerSimple() {
ModelNode operation = new ModelNode();
operation.get(ClientConstants.OP_ADDR).add("subsystem", "elytron").add(ElytronDescriptionConstants.SERVER_SSL_CONTEXT, INIT_TEST_SERVER_SSL_CONTEXT);
operation.get(ClientConstants.OP).set(ClientConstants.ADD);
operation.get(ElytronDescriptionConstants.KEY_MANAGER).set("ServerKeyManager");
operation.get(ElytronDescriptionConstants.PROVIDERS).set("ManagerProviderLoader");
Assert.assertEquals(SUCCESS, services.executeOperation(operation).get(OUTCOME).asString());

operation.get(ClientConstants.OP).set(ClientConstants.WRITE_ATTRIBUTE_OPERATION);
operation.get(ClientConstants.NAME).set(ElytronDescriptionConstants.OCSP_STAPLING);
operation.get(ClientConstants.VALUE).set(Collections.emptySet());
Assert.assertEquals(SUCCESS, services.executeOperation(operation).get(OUTCOME).asString());

operation.get(ClientConstants.OP).set(ClientConstants.WRITE_ATTRIBUTE_OPERATION);
operation.get(ClientConstants.NAME).set(ElytronDescriptionConstants.OCSP_STAPLING + "." + ElytronDescriptionConstants.RESPONSE_TIMEOUT);
operation.get(ClientConstants.VALUE).set(2500);
operation.get(ClientConstants.OP).set(ClientConstants.WRITE_ATTRIBUTE_OPERATION);
operation.get(ClientConstants.NAME).set(ElytronDescriptionConstants.OCSP_STAPLING + "." + ElytronDescriptionConstants.CACHE_SIZE);
operation.get(ClientConstants.VALUE).set(512);
operation.get(ClientConstants.OP).set(ClientConstants.WRITE_ATTRIBUTE_OPERATION);
operation.get(ClientConstants.NAME).set(ElytronDescriptionConstants.OCSP_STAPLING + "." + ElytronDescriptionConstants.CACHE_LIFETIME);
operation.get(ClientConstants.VALUE).set(7200);
operation.get(ClientConstants.OP).set(ClientConstants.WRITE_ATTRIBUTE_OPERATION);
operation.get(ClientConstants.NAME).set(ElytronDescriptionConstants.OCSP_STAPLING + "." + ElytronDescriptionConstants.RESPONDER_URI);
operation.get(ClientConstants.VALUE).set("http://localhost:8080/ocsp");
operation.get(ClientConstants.OP).set(ClientConstants.WRITE_ATTRIBUTE_OPERATION);
operation.get(ClientConstants.NAME).set(ElytronDescriptionConstants.OCSP_STAPLING + "." + ElytronDescriptionConstants.RESPONDER_OVERRIDE);
operation.get(ClientConstants.VALUE).set(true);
operation.get(ClientConstants.OP).set(ClientConstants.WRITE_ATTRIBUTE_OPERATION);
operation.get(ClientConstants.NAME).set(ElytronDescriptionConstants.OCSP_STAPLING + "." + ElytronDescriptionConstants.IGNORE_EXTENSIONS);
operation.get(ClientConstants.VALUE).set(true);
Assert.assertEquals(SUCCESS, services.executeOperation(operation).get(OUTCOME).asString());
}

@Test
public void testOcspStaplingClientSimple() {
ModelNode operation = new ModelNode();
operation.get(ClientConstants.OP_ADDR).add("subsystem", "elytron").add(ElytronDescriptionConstants.CLIENT_SSL_CONTEXT, INIT_TEST_CLIENT_SSL_CONTEXT);
operation.get(ClientConstants.OP).set(ClientConstants.ADD);
operation.get(ElytronDescriptionConstants.TRUST_MANAGER).set("CaTrustManager");
operation.get(ElytronDescriptionConstants.PROVIDERS).set("ManagerProviderLoader");
operation.get(ElytronDescriptionConstants.ACCEPT_OCSP_STAPLING).set(true);
operation.get(ElytronDescriptionConstants.OCSP_STAPLING_SOFT_FAIL).set(true);
Assert.assertEquals(SUCCESS, services.executeOperation(operation).get(OUTCOME).asString());
}

private SSLContext getSslContext(String contextName) {
return getSslContext(contextName, true);
}

Expand Down
Loading

0 comments on commit 4cad292

Please sign in to comment.