From ff90ab3b969127d37f8fd19eb4e25a38f89b49b7 Mon Sep 17 00:00:00 2001 From: Prarthona Paul Date: Wed, 3 Jul 2024 11:51:27 -0400 Subject: [PATCH] WFCORE-6802 [Preview] OCSP stapling support --- elytron/pom.xml | 1 + .../elytron/ElytronDescriptionConstants.java | 9 + .../extension/elytron/SSLDefinitions.java | 193 +++++++-- .../wildfly/extension/elytron/TlsParser.java | 60 +++ .../extension/elytron/TrivialService.java | 8 +- .../_private/ElytronSubsystemMessages.java | 2 + .../elytron/LocalDescriptions.properties | 9 + .../schema/wildfly-elytron_preview_18_0.xsd | 74 +++- .../extension/elytron/TlsTestCase.java | 55 ++- .../elytron-subsystem-preview-18.0.xml | 404 ++++++++++++++++++ 10 files changed, 781 insertions(+), 34 deletions(-) create mode 100644 elytron/src/test/resources/org/wildfly/extension/elytron/elytron-subsystem-preview-18.0.xml diff --git a/elytron/pom.xml b/elytron/pom.xml index e506f39254d..e1858af4213 100644 --- a/elytron/pom.xml +++ b/elytron/pom.xml @@ -414,6 +414,7 @@ jacc-with-providers.xml legacy*.xml elytron-subsystem-community*.xml + elytron-subsystem-preview*.xml src/main/resources/schema/wildfly-elytron_18_0.xsd diff --git a/elytron/src/main/java/org/wildfly/extension/elytron/ElytronDescriptionConstants.java b/elytron/src/main/java/org/wildfly/extension/elytron/ElytronDescriptionConstants.java index 87fd07f4ae6..d1d8d47f1be 100644 --- a/elytron/src/main/java/org/wildfly/extension/elytron/ElytronDescriptionConstants.java +++ b/elytron/src/main/java/org/wildfly/extension/elytron/ElytronDescriptionConstants.java @@ -12,6 +12,7 @@ */ interface ElytronDescriptionConstants { + String ACCEPT_OCSP_STAPLING = "accept-ocsp-stapling"; String ACCOUNT_KEY = "account-key"; String ACTION = "action"; String ACTIVE_SESSION_COUNT = "active-session-count"; @@ -73,6 +74,8 @@ interface ElytronDescriptionConstants { String BCRYPT_MAPPER = "bcrypt-mapper"; String CAA_IDENTITIES = "caa-identities"; + String CACHE_SIZE = "cache-size"; + String CACHE_LIFETIME = "cache-lifetime"; String CACHING_REALM = "caching-realm"; String CASE_PRINCIPAL_TRANSFORMER = "case-principal-transformer"; String CALLBACK_HANDLER = "callback-handler"; @@ -246,6 +249,7 @@ interface ElytronDescriptionConstants { String IDENTITY_MAPPING = "identity-mapping"; String IDENTITY_REALM = "identity-realm"; String IGNORE_UNAVAILABLE_REALMS = "ignore-unavailable-realms"; + String IGNORE_EXTENSIONS = "ignore-extensions"; String IMPLEMENTATION = "implementation"; String IMPLEMENTATION_PROPERTIES = "implementation-properties"; String IMPORT_CERTIFICATE = "import-certificate"; @@ -366,6 +370,8 @@ interface ElytronDescriptionConstants { String OBTAIN_CERTIFICATE = "obtain-certificate"; 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"; @@ -467,6 +473,9 @@ interface ElytronDescriptionConstants { String RESPONDER = "responder"; String RESPONDER_CERTIFICATE = "responder-certificate"; String RESPONDER_KEYSTORE = "responder-keystore"; + String RESPONDER_OVERRIDE = "responder-override"; + String RESPONDER_URI = "responder-uri"; + String RESPONSE_TIMEOUT = "response-timeout"; String REVERSE = "reverse"; String REVOKE_CERTIFICATE = "revoke-certificate"; String RIGHT = "right"; diff --git a/elytron/src/main/java/org/wildfly/extension/elytron/SSLDefinitions.java b/elytron/src/main/java/org/wildfly/extension/elytron/SSLDefinitions.java index e46fbfc91da..f9cab71fb10 100644 --- a/elytron/src/main/java/org/wildfly/extension/elytron/SSLDefinitions.java +++ b/elytron/src/main/java/org/wildfly/extension/elytron/SSLDefinitions.java @@ -232,6 +232,18 @@ class SSLDefinitions { //.setDefaultValue(new ModelNode(CipherSuiteSelector.OPENSSL_DEFAULT_CIPHER_SUITE_NAMES)) .build(); + static final SimpleAttributeDefinition ACCEPT_OCSP_STAPLING = new SimpleAttributeDefinitionBuilder(ElytronDescriptionConstants.ACCEPT_OCSP_STAPLING, ModelType.BOOLEAN, true) + .setAllowExpression(true) + .setRestartAllServices() + .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) @@ -398,6 +410,55 @@ class SSLDefinitions { .setRestartAllServices() .build(); + static final SimpleAttributeDefinition RESPONSE_TIMEOUT = new SimpleAttributeDefinitionBuilder(ElytronDescriptionConstants.RESPONSE_TIMEOUT, ModelType.INT, true) + .setValidator(new IntRangeValidator(1)) + .setDefaultValue(new ModelNode(5000)) + .setAllowExpression(true) + .setRestartAllServices() + .setStability(Stability.PREVIEW) + .build(); + + static final SimpleAttributeDefinition CACHE_SIZE = new SimpleAttributeDefinitionBuilder(ElytronDescriptionConstants.CACHE_SIZE, ModelType.INT, true) + .setDefaultValue(new ModelNode(256)) + .setAllowExpression(true) + .setRestartAllServices() + .setStability(Stability.PREVIEW) + .build(); + + static final SimpleAttributeDefinition CACHE_LIFETIME = new SimpleAttributeDefinitionBuilder(ElytronDescriptionConstants.CACHE_LIFETIME, ModelType.INT, true) + .setDefaultValue(new ModelNode(3600)) + .setAllowExpression(true) + .setRestartAllServices() + .setStability(Stability.PREVIEW) + .build(); + + static final SimpleAttributeDefinition RESPONDER_URI = new SimpleAttributeDefinitionBuilder(ElytronDescriptionConstants.RESPONDER_URI, ModelType.STRING, true) + .setRequires(ElytronDescriptionConstants.RESPONDER_OVERRIDE) + .setAllowExpression(true) + .setRestartAllServices() + .setStability(Stability.PREVIEW) + .build(); + + static final SimpleAttributeDefinition RESPONDER_OVERRIDE = new SimpleAttributeDefinitionBuilder(ElytronDescriptionConstants.RESPONDER_OVERRIDE, ModelType.BOOLEAN, true) + .setDefaultValue(ModelNode.FALSE) + .setAllowExpression(true) + .setRestartAllServices() + .setStability(Stability.PREVIEW) + .build(); + + static final SimpleAttributeDefinition IGNORE_EXTENSIONS = new SimpleAttributeDefinitionBuilder(ElytronDescriptionConstants.IGNORE_EXTENSIONS, ModelType.BOOLEAN, true) + .setDefaultValue(ModelNode.FALSE) + .setAllowExpression(true) + .setRestartAllServices() + .setStability(Stability.PREVIEW) + .build(); + + static final ObjectTypeAttributeDefinition OCSP_STAPLING = new ObjectTypeAttributeDefinition.Builder(ElytronDescriptionConstants.OCSP_STAPLING, RESPONSE_TIMEOUT, CACHE_SIZE, CACHE_LIFETIME, RESPONDER_URI, RESPONDER_OVERRIDE, IGNORE_EXTENSIONS) + .setRequired(false) + .setRestartAllServices() + .setStability(Stability.PREVIEW) + .build(); + /* * Runtime Attributes */ @@ -941,31 +1002,6 @@ private File resolveFileLocation(String path, String relativeTo, InjectedValue

(SSLContext.class, ServiceController.Mode.ACTIVE, ServiceController.Mode.PASSIVE, attributes, SSL_CONTEXT_RUNTIME_CAPABILITY) { @@ -1316,7 +1352,29 @@ protected ValueSupplier getValueSupplier(ServiceBuilder final int maximumSessionCacheSize = MAXIMUM_SESSION_CACHE_SIZE.resolveModelAttribute(context, model).asInt(); final int sessionTimeout = SESSION_TIMEOUT.resolveModelAttribute(context, model).asInt(); final boolean wrap = WRAP.resolveModelAttribute(context, model).asBoolean(); - + final String ocspStapling = OCSP_STAPLING.resolveModelAttribute(context, model).asStringOrNull(); + final int responseTimeout; + final int cacheSize; + final int cacheLifetime; + final String responderURI; + final boolean responderOverride; + final boolean ignoreExtensions; + + if (ocspStapling != null) { + responseTimeout = RESPONSE_TIMEOUT.resolveModelAttribute(context, model).asInt(); + cacheSize = CACHE_SIZE.resolveModelAttribute(context, model).asInt(); + cacheLifetime = CACHE_LIFETIME.resolveModelAttribute(context, model).asInt(); + responderURI = RESPONDER_URI.resolveModelAttribute(context, model).asString(); + responderOverride = RESPONDER_OVERRIDE.resolveModelAttribute(context, model).asBoolean(); + ignoreExtensions = IGNORE_EXTENSIONS.resolveModelAttribute(context, model).asBoolean(); + } else { + responseTimeout = 0; + cacheSize = 0; + cacheLifetime = 0; + responderURI = null; + responderOverride = false; + ignoreExtensions = false; + } return () -> { SecurityDomain securityDomain = securityDomainInjector.getOptionalValue(); X509ExtendedKeyManager keyManager = getX509KeyManager(keyManagerInjector.getOptionalValue()); @@ -1366,6 +1424,15 @@ protected ValueSupplier getValueSupplier(ServiceBuilder .setSessionTimeout(sessionTimeout) .setWrap(wrap); + if (ocspStapling != null) { + builder.setResponseTimeout(responseTimeout) + .setCacheSize(cacheSize) + .setCacheLifetime(cacheLifetime) + .setResponderURI(responderURI) + .setResponderOverride(responderOverride) + .setIgnoreExtensions(ignoreExtensions); + } + if (ROOT_LOGGER.isTraceEnabled()) { ROOT_LOGGER.tracef( "ServerSSLContext supplying: securityDomain = %s keyManager = %s trustManager = %s " @@ -1461,7 +1528,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}; + KEY_MANAGER, TRUST_MANAGER, providersDefinition, PROVIDER_NAME, ACCEPT_OCSP_STAPLING, OCSP_STAPLING_SOFT_FAIL}; AbstractAddStepHandler add = new TrivialAddHandler(SSLContext.class, attributes, SSL_CONTEXT_RUNTIME_CAPABILITY) { @Override @@ -1475,6 +1542,10 @@ protected ValueSupplier getValueSupplier(ServiceBuilder final List protocols = PROTOCOLS.unwrap(context, model); 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(); + final String trustManagerName = TRUST_MANAGER.resolveModelAttribute(context,model).asString(); + return () -> { X509ExtendedKeyManager keyManager = getX509KeyManager(keyManagerInjector.getOptionalValue()); X509ExtendedTrustManager trustManager = getX509TrustManager(trustManagerInjector.getOptionalValue()); @@ -1482,8 +1553,20 @@ protected ValueSupplier getValueSupplier(ServiceBuilder 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(); + revocationBuilder.setTrustManagerFactory(trustManagerFactory); + revocationBuilder.setTrustStore(getModifiableTrustManagerService(context, trustManagerName).getModifiableValue()); + + 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 list = new ArrayList<>(); @@ -1496,7 +1579,8 @@ protected ValueSupplier getValueSupplier(ServiceBuilder )); } builder.setClientMode(true) - .setWrap(false); + .setWrap(false) + .setAcceptOCSPStapling(acceptOCSPStapling); if (ROOT_LOGGER.isTraceEnabled()) { ROOT_LOGGER.tracef( @@ -1689,4 +1773,55 @@ public InjectedValue 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 ModifiableKeyStoreService getModifiableTrustManagerService(OperationContext context, String trustManagerName) throws OperationFailedException { + ServiceRegistry serviceRegistry = context.getServiceRegistry(false); + RuntimeCapability runtimeCapability = TRUST_MANAGER_RUNTIME_CAPABILITY.fromBaseCapability(trustManagerName); + ServiceName serviceName = runtimeCapability.getCapabilityServiceName(); + + ServiceController serviceContainer = getRequiredService(serviceRegistry, serviceName, TrustManager.class); + ServiceController.State serviceState = serviceContainer.getState(); + if (serviceState != ServiceController.State.UP) { + throw ROOT_LOGGER.requiredServiceNotUp(serviceName, serviceState); + } + + String keyStoreName = null; + Set serviceNames = serviceContainer.requires(); + for(ServiceName name : serviceNames) { + if (name.getCanonicalName().contains(KEY_STORE_CAPABILITY)) { + keyStoreName = (name).getCanonicalName().substring(KEY_STORE_CAPABILITY.length() + 1); + } + } + + if (keyStoreName == null) { + throw ROOT_LOGGER.unableToLoadKeystoreCapabilityService(); + } else { + return getModifiableKeyStoreService(context, keyStoreName); + } + } + } diff --git a/elytron/src/main/java/org/wildfly/extension/elytron/TlsParser.java b/elytron/src/main/java/org/wildfly/extension/elytron/TlsParser.java index cd8592d815e..51f13aef109 100644 --- a/elytron/src/main/java/org/wildfly/extension/elytron/TlsParser.java +++ b/elytron/src/main/java/org/wildfly/extension/elytron/TlsParser.java @@ -180,6 +180,30 @@ class TlsParser { .addAttribute(SSLDefinitions.FINAL_PRINCIPAL_TRANSFORMER) .addAttribute(SSLDefinitions.REALM_MAPPER); + private PersistentResourceXMLBuilder serverSslContextPreviewParser_18_0 = PersistentResourceXMLDescription.builder(PathElement.pathElement(SERVER_SSL_CONTEXT)) + .setXmlWrapperElement(SERVER_SSL_CONTEXTS) + .setMarshallDefaultValues(true) + .addAttribute(SSLDefinitions.SECURITY_DOMAIN) + .addAttribute(SSLDefinitions.CIPHER_SUITE_FILTER) + .addAttribute(SSLDefinitions.CIPHER_SUITE_NAMES) + .addAttribute(SSLDefinitions.PROTOCOLS) + .addAttribute(SSLDefinitions.WANT_CLIENT_AUTH) + .addAttribute(SSLDefinitions.NEED_CLIENT_AUTH) + .addAttribute(SSLDefinitions.AUTHENTICATION_OPTIONAL) + .addAttribute(SSLDefinitions.USE_CIPHER_SUITES_ORDER) + .addAttribute(SSLDefinitions.MAXIMUM_SESSION_CACHE_SIZE) + .addAttribute(SSLDefinitions.SESSION_TIMEOUT) + .addAttribute(SSLDefinitions.WRAP) + .addAttribute(SSLDefinitions.KEY_MANAGER) + .addAttribute(SSLDefinitions.TRUST_MANAGER) + .addAttribute(SSLDefinitions.PROVIDERS) + .addAttribute(SSLDefinitions.PROVIDER_NAME) + .addAttribute(SSLDefinitions.PRE_REALM_PRINCIPAL_TRANSFORMER) + .addAttribute(SSLDefinitions.POST_REALM_PRINCIPAL_TRANSFORMER) + .addAttribute(SSLDefinitions.FINAL_PRINCIPAL_TRANSFORMER) + .addAttribute(SSLDefinitions.REALM_MAPPER) + .addAttribute(SSLDefinitions.OCSP_STAPLING); // new OCSP_STAPLING element + private PersistentResourceXMLBuilder clientSslContextParser = PersistentResourceXMLDescription.builder(PathElement.pathElement(CLIENT_SSL_CONTEXT)) .setXmlWrapperElement(CLIENT_SSL_CONTEXTS) .addAttribute(SSLDefinitions.SECURITY_DOMAIN) @@ -224,6 +248,26 @@ class TlsParser { .addAttribute(SSLDefinitions.PROVIDERS) .addAttribute(SSLDefinitions.PROVIDER_NAME); + 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) + .addAttribute(SSLDefinitions.CIPHER_SUITE_NAMES) + .addAttribute(SSLDefinitions.PROTOCOLS) + .addAttribute(SSLDefinitions.WANT_CLIENT_AUTH) + .addAttribute(SSLDefinitions.NEED_CLIENT_AUTH) + .addAttribute(SSLDefinitions.AUTHENTICATION_OPTIONAL) + .addAttribute(SSLDefinitions.USE_CIPHER_SUITES_ORDER) + .addAttribute(SSLDefinitions.MAXIMUM_SESSION_CACHE_SIZE) + .addAttribute(SSLDefinitions.SESSION_TIMEOUT) + .addAttribute(SSLDefinitions.WRAP) + .addAttribute(SSLDefinitions.KEY_MANAGER) + .addAttribute(SSLDefinitions.TRUST_MANAGER) + .addAttribute(SSLDefinitions.PROVIDERS) + .addAttribute(SSLDefinitions.PROVIDER_NAME) + .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) .addAttribute(CertificateAuthorityAccountDefinition.CERTIFICATE_AUTHORITY) @@ -371,4 +415,20 @@ public void marshallSingleElement(AttributeDefinition attribute, ModelNode mappi .addChild(serverSslSniContextParser) .addChild(dynamicClientSslContextParser) // new .build(); + + final PersistentResourceXMLDescription tlsParserPreview_18_0 = decorator(TLS) + .addChild(decorator(KEY_STORES) + .addChild(keyStoreParser) + .addChild(ldapKeyStoreParser) + .addChild(filteringKeyStoreParser) + ) + .addChild(keyManagerParser_12_0) + .addChild(trustManagerParser_14_0) + .addChild(serverSslContextPreviewParser_18_0) // new parser with ocsp_stapling + .addChild(clientSslContextParserPreview_18_0) // new parser with ocsp_stapling + .addChild(certificateAuthorityParser) + .addChild(certificateAuthorityAccountParser) + .addChild(serverSslSniContextParser) + .addChild(dynamicClientSslContextParser) + .build(); } diff --git a/elytron/src/main/java/org/wildfly/extension/elytron/TrivialService.java b/elytron/src/main/java/org/wildfly/extension/elytron/TrivialService.java index 63dd86dfe36..e62e44c18f6 100644 --- a/elytron/src/main/java/org/wildfly/extension/elytron/TrivialService.java +++ b/elytron/src/main/java/org/wildfly/extension/elytron/TrivialService.java @@ -45,7 +45,11 @@ void setValueSupplier(ValueSupplier 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); } @@ -69,7 +73,7 @@ public T getValue() throws IllegalStateException, IllegalArgumentException { @FunctionalInterface interface ValueSupplier { - T get() throws StartException; + T get() throws Exception; default void dispose() {} diff --git a/elytron/src/main/java/org/wildfly/extension/elytron/_private/ElytronSubsystemMessages.java b/elytron/src/main/java/org/wildfly/extension/elytron/_private/ElytronSubsystemMessages.java index 5a1390105c3..84b39549042 100644 --- a/elytron/src/main/java/org/wildfly/extension/elytron/_private/ElytronSubsystemMessages.java +++ b/elytron/src/main/java/org/wildfly/extension/elytron/_private/ElytronSubsystemMessages.java @@ -732,5 +732,7 @@ public interface ElytronSubsystemMessages extends BasicLogger { * * If no suitable section is available add a new section. */ + @Message(id = 1221, value = "Unable to load keystore capability service from trustManager") + OperationFailedException unableToLoadKeystoreCapabilityService(); } diff --git a/elytron/src/main/resources/org/wildfly/extension/elytron/LocalDescriptions.properties b/elytron/src/main/resources/org/wildfly/extension/elytron/LocalDescriptions.properties index 36d6297c7e5..234221ca2a2 100644 --- a/elytron/src/main/resources/org/wildfly/extension/elytron/LocalDescriptions.properties +++ b/elytron/src/main/resources/org/wildfly/extension/elytron/LocalDescriptions.properties @@ -1386,6 +1386,8 @@ elytron.client-ssl-context.key-refresh=Refresh KeyManager used by SSLContext. elytron.client-ssl-context.trust-manager=Reference to the trust manager to use within the SSLContext. 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. @@ -1521,6 +1523,13 @@ elytron.server-ssl-context.ssl-session.peer-certificates.signature-algorithm=The elytron.server-ssl-context.ssl-session.peer-certificates.signature=The signature of the certificate. elytron.server-ssl-context.ssl-session.peer-certificates.version=The certificate version. +elytron.server-ssl-context.ocsp-stapling=Support for OCSP Stapling for server ssl context. +elytron.server-ssl-context.ocsp-stapling.response-timeout=Enables online certificate status protocol Stapling for the server SSL context. +elytron.server-ssl-context.ocsp-stapling.cache-size=Controls the maximum cache size in entries. +elytron.server-ssl-context.ocsp-stapling.cache-lifetime=Controls the maximum life of a cached response in seconds. +elytron.server-ssl-context.ocsp-stapling.responder-uri=The responder to contact in case the certificate used by the server does not have the Authority Info Access (AIA) extension. This does not override the AIA extension value unless "responder-override" is set to true. +elytron.server-ssl-context.ocsp-stapling.responder-override=Determines whether the Authority information from the AIA extension value would be overridden by the value of the `responderURI`. +elytron.server-ssl-context.ocsp-stapling.ignore-extensions=determines whether the forwarding of OCSP extensions specified in the "status_request" or "status_request_v2" TLS extensions is disabled or not. # Operations elytron.server-ssl-context.ssl-session.invalidate=Invalidate the SSLSession (Note: This does not terminate current connections, only prevents future connections from joining or resuming this session). diff --git a/elytron/src/main/resources/schema/wildfly-elytron_preview_18_0.xsd b/elytron/src/main/resources/schema/wildfly-elytron_preview_18_0.xsd index 10545bd6ff8..85406a0589f 100644 --- a/elytron/src/main/resources/schema/wildfly-elytron_preview_18_0.xsd +++ b/elytron/src/main/resources/schema/wildfly-elytron_preview_18_0.xsd @@ -5145,6 +5145,9 @@ Definitions of a single server side SSLContext. + + + @@ -5298,6 +5301,61 @@ + + + + Enables online certificate status protocol Stapling for the server SSL context. + + + + + + Controls the maximum amount of time in millisecond the server will use to obtain OCSP responses, + whether from the cache or by contacting an OCSP responder. + + + + + + + Controls the maximum cache size in entries. + + + + + + + Controls the maximum life of a cached response in seconds. + + + + + + + The responder to contact in case the certificate used by the server does + not have the Authority Info Access (AIA) extension. This does not override + the AIA extension value unless "responder-override" is set to true. + + + + + + + Determines whether the Authority information from the AIA extension + value would be overridden by the value of the `responderURI`. + + + + + + + determines whether the forwarding of OCSP extensions specified in the + "status_request" or "status_request_v2" TLS extensions is disabled or not. + + + + + @@ -5372,6 +5430,20 @@ + + + + Indicates whether the client would accept OCSP stapled responses fom the model or not. + + + + + + + Indicates the behaviour of the client when the stapled status of the server's certificate is unknown. + + + @@ -6438,4 +6510,4 @@ - + \ No newline at end of file diff --git a/elytron/src/test/java/org/wildfly/extension/elytron/TlsTestCase.java b/elytron/src/test/java/org/wildfly/extension/elytron/TlsTestCase.java index f84d56bd908..5c4a26b7780 100644 --- a/elytron/src/test/java/org/wildfly/extension/elytron/TlsTestCase.java +++ b/elytron/src/test/java/org/wildfly/extension/elytron/TlsTestCase.java @@ -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; @@ -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; @@ -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; @@ -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()); @@ -644,6 +648,53 @@ public void testOcspSimple() { MatcherAssert.assertThat(trustManager, CoreMatchers.instanceOf(X509RevocationTrustManager.class)); } + @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); } diff --git a/elytron/src/test/resources/org/wildfly/extension/elytron/elytron-subsystem-preview-18.0.xml b/elytron/src/test/resources/org/wildfly/extension/elytron/elytron-subsystem-preview-18.0.xml new file mode 100644 index 00000000000..37eab49a1e5 --- /dev/null +++ b/elytron/src/test/resources/org/wildfly/extension/elytron/elytron-subsystem-preview-18.0.xml @@ -0,0 +1,404 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file