As this documentation is primarily intended for users migrating to WildFly Elytron I am going to jump straight into the configuration required with WildFly Elytron.
This section will cover how to create the various resources required to achieve CLIENT_CERT authentication with fallback to username / password authentication for both HTTP and SASL (i.e. Remoting) - both are being covered at the same time as predominantly they require the same core configuration, it is not until the definition of the authentication factories that the configuration becomes really specific.
The WildFly Elytron project already contains a dummy certificate authority set up that we use for testing, the KeyStores within this documentation are from the dummy certificate authority although for anything other than testing these should be replaced with your own.
As a first step we define some paths that point to the wildfly-elytron project so we can use these in the subsequent configuration.
./path=elytron.project:add(path=/home/darranl/src/wildfly10/wildfly-elytron)
./path=elytron.project.jks:add(path=src/test/resources/ca/jks, relative-to=elytron.project)
./path=elytron.project.properties:add(path=src/test/resources/org/wildfly/security/auth/realm, relative-to=elytron.project)
This results in the following configuration.
<paths>
<path name="elytron.project" path="/home/darranl/src/wildfly10/wildfly-elytron"/>
<path name="elytron.project.jks" path="src/test/resources/ca/jks" relative-to="elytron.project"/>
<path name="elytron.project.properties" path="src/test/resources/org/wildfly/security/auth/realm" relative-to="elytron.project"/>
</paths>
KeyStores, KeyManagers, and TrustManagers.
The next step is to define the KeyStore resources, for this example three different keystores are used: -
-
localhost.keystore - Contains the servers key and certificate for 'localhost'.
-
beetles.keystore - Contains the individual client certificates.
-
ca.keystore - Contains the certificate of the certificate authority.
When we define the overall configuration we will use the localhost keystore along with the ca keystore for the incoming connections so initially all client certificates signed by the certificate authority will be accepted and subsequently a security realm will check it against the actual certificates within beetles keystore.
./subsystem=elytron/key-store=localhost:add(type=jks, relative-to=elytron.project.jks, path=localhost.keystore, credential-reference={clear-text=Elytron})
./subsystem=elytron/key-store=beetles:add(type=jks, relative-to=elytron.project.jks, path=beetles.keystore, credential-reference={clear-text=Elytron})
./subsystem=elytron/key-store=ca:add(type=jks, relative-to=elytron.project.jks, path=ca.truststore, credential-reference={clear-text=Elytron})
This results in the following definitions: -
<subsystem xmlns="urn:wildfly:elytron:1.1" final-providers="combined-providers" disallowed-providers="OracleUcrypto">
...
<tls>
<key-stores>
<key-store name="localhost">
<credential-reference clear-text="Elytron"/>
<implementation type="jks"/>
<file path="localhost.keystore" relative-to="elytron.project.jks"/>
</key-store>
<key-store name="beetles">
<credential-reference clear-text="Elytron"/>
<implementation type="jks"/>
<file path="beetles.keystore" relative-to="elytron.project.jks"/>
</key-store>
<key-store name="ca">
<credential-reference clear-text="Elytron"/>
<implementation type="jks"/>
<file path="ca.truststore" relative-to="elytron.project.jks"/>
</key-store>
</key-stores>
</tls>
</subsystem>
Next the key and trust manager resources will be defined using these keystores.
./subsystem=elytron/key-manager=localhost-manager:add(algorithm=SunX509, key-store=localhost, credential-reference={clear-text=Elytron})
./subsystem=elytron/trust-manager=ca-manager:add(algorithm=SunX509, key-store=ca)
Resulting in: -
<subsystem xmlns="urn:wildfly:elytron:1.1" final-providers="combined-providers" disallowed-providers="OracleUcrypto">
...
<tls>
...
<key-managers>
<key-manager name="localhost-manager" algorithm="SunX509" key-store="localhost">
<credential-reference clear-text="Elytron"/>
</key-manager>
</key-managers>
<trust-managers>
<trust-manager name="ca-manager" algorithm="SunX509" key-store="ca"/>
</trust-managers>
</tls>
</subsystem>
Realms and Domains
Two security realms are now defined, one of these uses properties files from within the WildFly Elytron project to support username/password authentication and the other using the clients certificates for verification.
./subsystem=elytron/properties-realm=test-users:add(users-properties={relative-to=elytron.project.properties, path=clear.properties, plain-text=true, digest-realm-name=ManagementRealm}, groups-properties={relative-to=elytron.project.properties, path=groups.properties})
./subsystem=elytron/key-store-realm=key-store-realm:add(key-store=beetles)
Resulting in: -
<subsystem xmlns="urn:wildfly:elytron:1.1" final-providers="combined-providers" disallowed-providers="OracleUcrypto">
...
<security-realms>
...
<key-store-realm name="key-store-realm" key-store="beetles"/>
...
<properties-realm name="test-users">
<users-properties path="clear.properties" relative-to="elytron.project.properties" digest-realm-name="ManagementRealm" plain-text="true"/>
<groups-properties path="groups.properties" relative-to="elytron.project.properties"/>
</properties-realm>
</security-realms>
...
</subsystem>
These security realms can now be referenced from a security domain: -
./subsystem=elytron/security-domain=client-cert-domain:add(realms=[{realm=test-users},{realm=key-store-realm}], \
default-realm=test-users, \
permission-mapper=default-permission-mapper)
Resulting in: -
<subsystem xmlns="urn:wildfly:elytron:1.1" final-providers="combined-providers" disallowed-providers="OracleUcrypto">
...
<security-domains>
...
<security-domain name="client-cert-domain" default-realm="test-users" permission-mapper="default-permission-mapper">
<realm name="test-users"/>
<realm name="key-store-realm"/>
</security-domain>
</security-domains>
...
</subsystem>
Before moving onto the individual authentication factories a couple of additional utility resources are also required: -
./subsystem=elytron/constant-realm-mapper=key-store-realm:add(realm-name=key-store-realm)
./subsystem=elytron/x500-attribute-principal-decoder=x500-decoder:add(attribute-name=CN, maximum-segments=1)
Resulting in: -
<subsystem xmlns="urn:wildfly:elytron:1.1" final-providers="combined-providers" disallowed-providers="OracleUcrypto">
...
<mappers>
...
<x500-attribute-principal-decoder name="x500-decoder" attribute-name="CN" maximum-segments="1"/>
...
<constant-realm-mapper name="key-store-realm" realm-name="key-store-realm"/>
...
</mappers>
...
</subsystem>
HTTP Authentication Factory
For the HTTP connections we now define a HTTP authentication factory using the previously defined resources and it is configured to support CLIENT_CERT and DIGEST authentication.
./subsystem=elytron/http-authentication-factory=client-cert-digest:add(http-server-mechanism-factory=global, \
security-domain=client-cert-domain, \
mechanism-configurations=[{ \
mechanism-name=CLIENT_CERT, \
realm-mapper=key-store-realm, \
pre-realm-principal-transformer=x500-decoder}, \
{mechanism-name=DIGEST, mechanism-realm-configurations=[{realm-name=ManagementRealm}]}])
Resulting in: -
<subsystem xmlns="urn:wildfly:elytron:1.1" final-providers="combined-providers" disallowed-providers="OracleUcrypto">
...
<http>
...
<http-authentication-factory name="client-cert-digest" http-server-mechanism-factory="global" security-domain="client-cert-domain">
<mechanism-configuration>
<mechanism mechanism-name="CLIENT_CERT" pre-realm-principal-transformer="x500-decoder" realm-mapper="key-store-realm"/>
<mechanism mechanism-name="DIGEST">
<mechanism-realm realm-name="ManagementRealm"/>
</mechanism>
</mechanism-configuration>
</http-authentication-factory>
...
</http>
...
</subsystem>
Where DIGEST authentication is used we rely on the default configuration within the security domain to select the 'test-users' realm, however where CLIENT_CERT authentication is in use an alternative realm-mapper is referenced to ensure the 'key-store-realm' is used.
Additionally for CLIENT_CERT authentication a principal-transformer is referenced to extract the CN attribute from the distinguished name of the client certificate and use this when accessing the identity from the security realm.
SASL Authentication Factory
The architecture of the two authentication factories if very similar so a SASL authentication factory can be defined in the same way as the HTTP equivalent.
./subsystem=elytron/sasl-authentication-factory=client-cert-digest:add(sasl-server-factory=elytron, \
security-domain=client-cert-domain, \
mechanism-configurations=[{mechanism-name=EXTERNAL, \
realm-mapper=key-store-realm, \
pre-realm-principal-transformer=x500-decoder}, \
{mechanism-name=DIGEST-MD5, mechanism-realm-configurations=[{realm-name=ManagementRealm}]}])
This results in: -
<subsystem xmlns="urn:wildfly:elytron:1.1" final-providers="combined-providers" disallowed-providers="OracleUcrypto">
...
<sasl>
...
<sasl-authentication-factory name="client-cert-digest" sasl-server-factory="elytron" security-domain="client-cert-domain">
<mechanism-configuration>
<mechanism mechanism-name="EXTERNAL" pre-realm-principal-transformer="x500-decoder" realm-mapper="key-store-realm"/>
<mechanism mechanism-name="DIGEST-MD5">
<mechanism-realm realm-name="ManagementRealm"/>
</mechanism>
</mechanism-configuration>
</sasl-authentication-factory>
...
</sasl>
...
</subsystem>
Realm mappers and principal transformers are defined in the same way as were defined for HTTP.
SSL Context
An SSL context is also defined for use by the server.
./subsystem=elytron/server-ssl-context=localhost:add(key-manager=localhost-manager, trust-manager=ca-manager, \
security-domain=client-cert-domain, \
authentication-optional=true, \
want-client-auth=true, \
need-client-auth=false)
Resulting in: -
<subsystem xmlns="urn:wildfly:elytron:1.1" final-providers="combined-providers" disallowed-providers="OracleUcrypto">
...
<tls>
...
<server-ssl-contexts>
<server-ssl-context name="localhost" security-domain="client-cert-domain" want-client-auth="true" need-client-auth="false" authentication-optional="true" key-manager="localhost-manager" trust-manager="ca-manager"/>
</server-ssl-contexts>
</tls>
</subsystem>
As we will be supporting fallback to username/password authentication need-client-auth is set to false as well as authentication-optional being set to false, this allows connections to be established but an alternative form of authentication will be required.