Skip to content

Commit

Permalink
ELY-434 [Preview] OCSP Stapling Support
Browse files Browse the repository at this point in the history
  • Loading branch information
Prarthona Paul committed Aug 19, 2024
1 parent 657ba2b commit 279f4ab
Show file tree
Hide file tree
Showing 10 changed files with 606 additions and 64 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,8 @@ private enum Version {
VERSION_1_4("urn:elytron:client:1.4", VERSION_1_3),
VERSION_1_5("urn:elytron:client:1.5", VERSION_1_4),
VERSION_1_6("urn:elytron:client:1.6", VERSION_1_5),
VERSION_1_7("urn:elytron:client:1.7", VERSION_1_6);
VERSION_1_7("urn:elytron:client:1.7", VERSION_1_6),
VERSION_1_8("urn:elytron:client:1.8", VERSION_1_7);

final String namespace;

Expand Down Expand Up @@ -465,6 +466,7 @@ private static void parseSslContextType(final ConfigurationXMLStreamReader reade
ExceptionSupplier<KeyStore, ConfigXMLParseException> trustStoreSupplier = null;
DeferredSupplier<Provider[]> providersSupplier = new DeferredSupplier<>(providers);
TrustManagerBuilder trustManagerBuilder = new TrustManagerBuilder(providersSupplier, location);
boolean acceptOcspStapling = false;

while (reader.hasNext()) {
final int tag = reader.nextTag();
Expand Down Expand Up @@ -536,6 +538,13 @@ private static void parseSslContextType(final ConfigurationXMLStreamReader reade
parseCertificateRevocationLists(reader, trustManagerBuilder, xmlVersion);
break;
}
case "accept-ocsp-stapling": {
if (isSet(foundBits, 10)) throw reader.unexpectedElement();
foundBits = setBit(foundBits, 10);
if (!xmlVersion.isAtLeast(Version.VERSION_1_8)) throw reader.unexpectedElement();
acceptOcspStapling = parseOcspStaplingType(reader, trustManagerBuilder, xmlVersion, keyStoresMap);
break;
}
default: throw reader.unexpectedElement();
}
} else if (tag != END_ELEMENT) {
Expand All @@ -549,6 +558,8 @@ private static void parseSslContextType(final ConfigurationXMLStreamReader reade
final ExceptionSupplier<X509ExtendedKeyManager, ConfigXMLParseException> finalKeyManagerSupplier = keyManagerSupplier;
final ExceptionSupplier<KeyStore, ConfigXMLParseException> finalTrustStoreSupplier = trustStoreSupplier;
final boolean initTrustManager = finalTrustStoreSupplier != null || isSet(foundBits, 7);
final boolean finalAcceptOcspStapling = acceptOcspStapling;

sslContextsMap.putIfAbsent(name, () -> {
final SSLContextBuilder sslContextBuilder = new SSLContextBuilder();
sslContextBuilder.setClientMode(true);
Expand All @@ -574,6 +585,7 @@ private static void parseSslContextType(final ConfigurationXMLStreamReader reade
sslContextBuilder.setProviderName(finalProviderName);
sslContextBuilder.setProviderSupplier(finalProvidersSupplier);
sslContextBuilder.setUseCipherSuitesOrder(true);
sslContextBuilder.setAcceptOCSPStapling(finalAcceptOcspStapling);
return sslContextBuilder.build();
});
return;
Expand All @@ -582,6 +594,56 @@ private static void parseSslContextType(final ConfigurationXMLStreamReader reade
throw reader.unexpectedDocumentEnd();
}

private static boolean parseOcspStaplingType(ConfigurationXMLStreamReader reader, TrustManagerBuilder builder, Version xmlVersion, Map<String, ExceptionSupplier<KeyStore, ConfigXMLParseException>> keyStoresMap) throws ConfigXMLParseException {
final int attributeCount = reader.getAttributeCount();
boolean acceptOcspStapling = false;
boolean softFail = false;
boolean gotResponderCertAlias = false;
boolean gotResponderKeystore = false;

for (int i = 0; i < attributeCount; i ++) {
checkAttributeNamespace(reader, i);
switch (reader.getAttributeLocalName(i)) {
case "accept-ocsp": {
if (acceptOcspStapling) throw reader.unexpectedAttribute(i);
if (!xmlVersion.isAtLeast(Version.VERSION_1_8)) throw reader.unexpectedAttribute(i);
acceptOcspStapling = reader.getBooleanAttributeValueResolved(i);
builder.setOcspStapling(acceptOcspStapling);
break;
}
case "soft-fail": {
if (softFail) throw reader.unexpectedAttribute(i);
if (!xmlVersion.isAtLeast(Version.VERSION_1_8)) throw reader.unexpectedAttribute(i);
softFail = reader.getBooleanAttributeValueResolved(i);
builder.setSoftFail(softFail);
break;
}
case "responder-certificate": {
if (gotResponderCertAlias) throw reader.unexpectedAttribute(i);
builder.setOcspRescponderCertAlias(reader.getAttributeValueResolved(i));
gotResponderCertAlias = true;
break;
}
case "responder-keystore": {
if (gotResponderKeystore) throw reader.unexpectedAttribute(i);
builder.setOcspResponderCertKeystoreSupplier(keyStoresMap.get(reader.getAttributeValueResolved(i)));
gotResponderKeystore = true;
break;
}
default: throw reader.unexpectedAttribute(i);
}
}
while (reader.hasNext()) {
final int tag = reader.nextTag();
if (tag == END_ELEMENT) {
return acceptOcspStapling;
} else {
throw reader.unexpectedContent();
}
}
throw reader.unexpectedDocumentEnd();
}

private static class TrustManagerBuilder {
final Supplier<Provider[]> providers;
final Location xmlLocation;
Expand All @@ -592,6 +654,7 @@ private static class TrustManagerBuilder {
List<InputStream> crlStreams = new ArrayList<>();
int maxCertPath = 5;
boolean ocsp = false;
boolean ocspStapling = false;
boolean preferCrls = false;
boolean onlyLeafCert = false;
boolean softFail = false;
Expand Down Expand Up @@ -638,6 +701,9 @@ boolean isMaxCertPathSet() {
public void setOcsp() {
this.ocsp = true;
}
public void setOcspStapling(boolean ocspStapling) {
this.ocspStapling = ocspStapling;
}

public void setPreferCrls(boolean preferCrls) {
this.preferCrls = preferCrls;
Expand Down Expand Up @@ -697,6 +763,15 @@ X509TrustManager build() throws NoSuchAlgorithmException, KeyStoreException, Con
revocationBuilder.setOcspResponderCert((X509Certificate) responderStore.getCertificate(responderCertAlias));
}

return revocationBuilder.build();
} else if (ocspStapling) {
X509RevocationTrustManager.Builder revocationBuilder = X509RevocationTrustManager.builder();
revocationBuilder.setTrustManagerFactory(trustManagerFactory);
revocationBuilder.setTrustStore(trustStore);
revocationBuilder.setCheckRevocation(true);
revocationBuilder.setSoftFail(softFail);
KeyStore responderStore = responderStoreSupplier != null ? responderStoreSupplier.get() : trustStore;
revocationBuilder.setOcspResponderCert((X509Certificate) responderStore.getCertificate(responderCertAlias));
return revocationBuilder.build();
} else {
trustManagerFactory.init(trustStore);
Expand Down
43 changes: 41 additions & 2 deletions auth/client/src/main/resources/schema/elytron-client-1_8.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
-->

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="urn:elytron:client:1.8"
xmlns="urn:elytron:client:1.8"
targetNamespace="urn:elytron:client:1.7"
xmlns="urn:elytron:client:1.7"
elementFormDefault="qualified"
attributeFormDefault="unqualified"
version="1.0">
Expand Down Expand Up @@ -100,6 +100,13 @@
<xsd:element name="certificate-revocation-list" type="certificate-revocation-list-type" minOccurs="0" />
<xsd:element name="certificate-revocation-lists" type="certificate-revocation-lists-type" minOccurs="0" />
<xsd:element name="ocsp" type="ocsp-type" minOccurs="0"/>
<xsd:element name="accept-ocsp-stapling" type="accept-ocsp-stapling-type" minOccurs="0" default="false">
<xsd:annotation>
<xsd:documentation>
Indicated whether the client will accept OCSP stapled responses from the server.
</xsd:documentation>
</xsd:annotation>
</xsd:element>
</xsd:all>
<xsd:attribute name="name" type="xsd:string" use="required">
<xsd:annotation>
Expand All @@ -121,6 +128,38 @@
</xsd:sequence>
</xsd:complexType>

<xsd:complexType name="accept-ocsp-stapling-type">
<xsd:attribute name="accept-ocsp" type="xsd:boolean" use="optional" default="false">
<xsd:annotation>
<xsd:documentation>
Indicates whether the client accepts ocsp stapled requests from the server.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="soft-fail" type="xsd:boolean" use="optional" default="true">
<xsd:annotation>
<xsd:documentation>
Determines the client's behaviour upon receiving a certificate with unknown
revocation status from the server.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="responder-certificate" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation>
Alias of OCSP Responder certificate.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="responder-keystore" type="xsd:string" use="optional">
<xsd:annotation>
<xsd:documentation>
Keystore for OCSP Responder certificate. trust-manager keystore is used by default and responder-certificate has to be defined.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:complexType>

<xsd:complexType name="abstract-match-rule-type" abstract="true">
<xsd:all>
<xsd:element ref="abstract-match-user" minOccurs="0"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,14 @@ public void testCipherSuites() throws Exception {
checkSSLContext(authContext, "http://names-only.org");
}

@Test
public void checkSSLContextWithOCSPStapling() throws Exception {
URL config = getClass().getResource("test-wildfly-config-v1_8.xml");
SecurityFactory<AuthenticationContext> authContext = ElytronXmlParser.parseAuthenticationClientConfiguration(config.toURI());
Assert.assertNotNull(authContext);
Assert.assertNotNull(authContext.create().getSslRules());
}

private void checkSSLContext(SecurityFactory<AuthenticationContext> authContext, String uri) throws Exception {
RuleNode<SecurityFactory<SSLContext>> node = authContext.create().sslRuleMatching(new URI(uri), null, null);
Assert.assertNotNull(node);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
JBoss, Home of Professional Open Source
Copyright 2017, Red Hat, Inc. and/or its affiliates, and individual
contributors by the @authors tag. See the copyright.txt in the
distribution for a full listing of individual contributors.
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.
-->
<configuration>
<authentication-client xmlns="urn:elytron:client:1.8">
<authentication-configurations>
<configuration name="clear-cred">
<credentials>
<clear-password password="password"/>
</credentials>
</configuration>
<configuration name="masked-cred">
<credentials>
<masked-password iteration-count="12" salt="12345678" masked-password="j37uUs8kG9t2QSWjoxxtDg=="/>
</credentials>
</configuration>
</authentication-configurations>
<key-stores>
<key-store name="client-keystore" type="JKS">
<file name="./target/keystore/client.keystore"/>
<key-store-clear-password password="password"/>
</key-store>
<key-store name="masked-keystore" type="JKS">
<file name="./target/keystore/client.keystore"/>
<key-store-masked-password algorithm="masked-HMAC-SHA1-AES-128" key-material="testingKeyMaterial" iteration-count="12" salt="12345678" masked-password="9EQZkSe7w+VHasVXbNxuJA==" initialization-vector="QXRFBhXc7Z8dkQaFn1yRzQ=="/>
</key-store>
<key-store name="empty-type-keystore">
<file name="./target/keystore/client.keystore"/>
<key-store-clear-password password="password"/>
</key-store>
</key-stores>
<ssl-contexts>
<ssl-context name="client-ssl-context-clear">
<key-store-ssl-certificate key-store-name="client-keystore" alias="testclient1">
<key-store-clear-password password="password"/>
</key-store-ssl-certificate>
<accept-ocsp-stapling accept-ocsp="true" soft-fail="true"/>
</ssl-context>
</ssl-contexts>
<authentication-rules>
<rule use-configuration="clear-cred">
<match-host name="clear"/>
</rule>
</authentication-rules>
<ssl-context-rules>
<rule use-ssl-context="client-ssl-context-clear">
<match-host name="clear"/>
</rule>
</ssl-context-rules>
</authentication-client>
</configuration>
Original file line number Diff line number Diff line change
Expand Up @@ -193,4 +193,7 @@ interface ElytronMessages extends BasicLogger {

@Message(id = 15001, value = "No '%s' provided by the configured providers")
NoSuchAlgorithmException noSslContextProvided(String type);

@Message(id = 15002, value = "ResponderURI needs to be provided to override AIA extension.")
IllegalArgumentException responderURIRequired();
}
Loading

0 comments on commit 279f4ab

Please sign in to comment.