Skip navigation
2019

Ashley Abdel-Sayed's Blog

September 2019 Previous month Next month

Elytron currently supports aggregate security realms which are an aggregate of an authentication-realm and one or more authorization-realms. The authentication-realm is used to load the credentials of an identity or perform evidence verification and the authorization-realms are used to load the identity’s attributes used for authorization. The principal undergoes a series of transformations before being used to load the authentication identity from the aggregate-realm.

 

The aggregate-realm now supports the attribute principal-transformer. This attribute is optional and allows the user to configure an additional transformation for the principal to undergo after the authentication identity is loaded but before the authorization identity is loaded.

 

The principal-transformer attribute references a transformer defined in the mappers configuration. This transformer can then be used when configuring an aggregate realm.

 

This blog post will be making use of:

 

elytron-examples/aggregate-realm-principal-transformer at master · wildfly-security-incubator/elytron-examples · GitHub

 

This example shows how to configure an aggregate-realm with a principal-transformer and how to secure a webapp using the aggregated realm for authentication and authorization.

 

Configuring the server

 

A WildFly CLI script that contains all of the commands that are used in this example can be found in the aggregate-realm-principal-transformer project in the elytron-examples repository.

 

Our first step will be to create our authentication and authorization realms, both of which will be filesystem realms.

 

The following command will create the authentication realm:

 

/subsystem=elytron/filesystem-realm=authenticationRealm:add(path=authenticationFS,relative-to=jboss.server.config.dir,encoded=false)

 

We will then add the identity with the name “guest” and password “guestPwd1!” to the authentication realm:

 

/subsystem=elytron/filesystem-realm=authenticationRealm:add-identity(identity="guest")

 

/subsystem=elytron/filesystem-realm=authenticationRealm:set-password(identity="guest", clear={password="guestPwd1!"})

 

Next, we create our authorization realm and add an identity with the name “guest-attributes” and the attribute ‘Roles=Users’:

 

/subsystem=elytron/filesystem-realm=authorizationRealm:add(path=authorizationFS,relative-to=jboss.server.config.dir,encoded=false)

 

/subsystem=elytron/filesystem-realm=authorizationRealm:add-identity(identity="guest-attributes")

 

/subsystem=elytron/filesystem-realm=authorizationRealm:add-identity-attribute(identity="guest-attributes",name=Roles,value=[Users])

 

We now have our two realms to be aggregated: authenticationRealm to load the credentials for the identity and authorizationRealm to load the attributes used for authorization. However, the principal for the identities is different for the two realms! To remedy this, we can add a constant principal transformer which will always return the principal “guest-attributes”:

 

/subsystem=elytron/constant-principal-transformer=constantTransformer:add(constant="guest-attributes")

 

We can then configure our aggregate realm using our two new filesystem realms and principal transformer:

 

/subsystem=elytron/aggregate-realm=aggregateRealm:add(authentication-realm=authenticationRealm,authorization-realm=authorizationRealm,principal-transformer=constantTransformer)

 

Now our aggregate realm will use  the principal “guest” to load the credentials from the authentication realm and then transform the principal to “guest-attributes” to load the attributes from the authorization realm.

 

We then add a security domain which makes use of our aggregate realm:

 

/subsystem=elytron/security-domain=simpleDomain:add(realms=[{realm=aggregateRealm}],default-realm=aggregateRealm,permission-mapper=default-permission-mapper)

 

We also configure a http-authentication-factory to secure our web application with our new domain:

 

/subsystem=elytron/http-authentication-factory=http-auth:add(http-server-mechanism-factory=global,security-domain=simpleDomain,mechanism-configurations=[{mechanism-name=BASIC,mechanism-realm-configurations=[{realm-name=other}]}])

 

/subsystem=undertow/application-security-domain=other:add(http-authentication-factory=http-auth)

 

Finally, we reload the server:

 

reload

 

Note: There is now the option to aggregate the attributes of an identity from multiple authorization realms. The principal used for all the authorization realms will be the same one (i.e. the transformed principal if a principal transformer is defined).

 

Deploying and accessing the application

 

We’re going to make use of the simple-webapp project. This application allows you to access a secured servlet if your identity has the attribute Roles=Users.  It can be deployed using the following commands:

 

cd $PATH_TO_ELYTRON_EXAMPLES/simple-webapp

mvn clean install wildfly:deploy

 

You can access the application at http://localhost:8080/simple-webapp/.

Try logging in with the username: “guest” and password: “guestPwd1!”

You are successfully authorized to access the secured application, which means our transformer worked and the attribute Roles=Users was successfully loaded from the authorization-realm using the principal that was transformed from ‘guest’ to ‘guest-attributes’.

 

Summary

This blog post has shown how to use the new optional attribute principal-transformer in an aggregate-realm to transform the principal between the authentication-realm and authorization-realm.

 

For more information on aggregate realms checkout:

 

wildfly/Aggregate_Security_Realm.adoc at master · wildfly/wildfly · GitHub

The Elytron Client allows remote clients to authenticate using Elytron. The configurations for these clients can be specified in two ways: using an XML configuration file or using a programmatic approach.

 

With WildFly 18, it is now possible to specify masked passwords as credential passwords and key store passwords for client authentication.

 

This blog post will demonstrate how to generate a masked password using the WildFly PasswordFactory and how to use this masked password as a credential in the authentication client using the two configuration methods.

 

Generating a Masked Password

 

A masked password consists of the following attributes:

 

  • `algorithm` The algorithm used to encrypt the password. If this attribute is not specified, the default value is "masked-MD5-DES".
  • `key-material` The initial key material used to encrypt the password. If this attribute is not specified, the default value is "somearbitrarycrazystringthatdoesnotmatter".
  • `iteration-count` The iteration count used to encrypt the password. This attribute is required.
  • `salt` The salt used to encrypt the password. This attribute is required.
  • `masked-password` The base64 encrypted password (without the "MASK-" prefix).
  • `initialization-vector` The initialization vector used to encrypt the password. This attribute is optional.

 

A list of the supported algorithms can be found at Passwords.

 

For our example, we will be generating a masked-MD5-DES type password. We can generate this password as follows:

 

static final Provider ELYTRON_PROVIDER = new WildFlyElytronProvider();

public static void main(String[] args) throws Exception {

   String algorithm = "masked-MD5-DES";
   char[] keyMaterial = "somearbitrarycrazystringthatdoesnotmatter".toCharArray();
   byte[] salt = "12345678".getBytes();
   int iterationCount = 100;
   String clearPassword = "password1!";
   byte[] initializationVector = null;

   PasswordFactory passwordFactory = PasswordFactory.getInstance(algorithm, ELYTRON_PROVIDER);

   MaskedPasswordAlgorithmSpec maskedAlgorithmSpec = new MaskedPasswordAlgorithmSpec(key, iterationCount, salt);
   EncryptablePasswordSpec encryptableSpec = new EncryptablePasswordSpec(clearPassword.toCharArray(), maskedAlgorithmSpec);

   MaskedPassword original = (MaskedPassword) passwordFactory.generatePassword(encryptableSpec);

   MaskedPasswordSpec maskedPasswordSpec = new MaskedPasswordSpec(keyMaterial, iterationCount, salt, masked);
   
   //Get the masked password as a string
   String maskedPassword = ByteIterator.ofBytes(maskedPasswordSpec.getMaskedPasswordBytes()).base64Encode().drainToString();
   System.out.println(String.format("Masked Password: " + maskedPassword));

   //Verify the masked password is the encryption of the clear password
   MaskedPassword restored = (MaskedPassword) passwordFactory.generatePassword(maskedPasswordSpec);
   System.out.println(String.format("Password Verified '%b'", passwordFactory.verify(restored, clearPassword.toCharArray())));

}

When encrypting “password1!” with the attributes defined as above we get the encrypted password “/Nym2s/dssMrabfdIGsZfQ==”.

 

Example Authentication Using Configuration File

 

We can now use the masked password in our client xml configuration. We will need to specify the attributes used to generate the password as well. Because we used the default values for the initializationKeyMaterial and the algorithm, we can simply specify the mandatory attributes:  

 

<configuration>

   <authentication-client xmlns="urn:elytron:client:1.4">

       <authentication-rules>

           <rule use-configuration="masked-config">

               <match-host name="masked"/>

           </rule>

       </authentication-rules>

       <authentication-configurations>

           <configuration name="masked-config">

               <set-user-name name="Guest"/>

               <credentials>

                   <masked-password iteration-count="100" salt="12345678" masked-password="/Nym2s/dssMrabfdIGsZfQ=="/>

               </credentials>

               <sasl-mechanism-selector selector="PLAIN"/>

           </configuration>

       </authentication-configurations>

   </authentication-client>

</configuration>

 

Now when authenticating, a client using this configuration can successfully use the username “User” and the password that was encrypted “password1!”.

 

Example Authentication Using Programmatic Approach

 

We can also create the same configuration programmatically and specify a masked password

 

AuthenticationConfiguration maskedConfig = AuthenticationConfiguration.empty()
                                               .setSaslMechanismSelector(SaslMechanismSelector.NONE.addMechanism(PLAIN))
                                               .useName(“Guest”)
                                               //algorithm, initial key material, and initialization vector are null
                                               .useMaskedPassword("/Nym2s/dssMrabfdIGsZfQ==", null, null, 100, "12345678", null);

AuthenticationContext context = AuthenticationContext.empty();
context = context.with(MatchRule.ALL.matchHost("masked"), maskedConfig);

 

Summary

 

This blog post showed how to generate a masked password. It also showed how to specify a masked password for the authentication credentials using a  client XML configuration and programmatically specifying the configuration.

 

For more information on the authentication client, checkout:

https://github.com/wildfly/wildfly/blob/master/docs/src/main/asciidoc/_client-guide/authentication-client.adoc#authentication-client----wildfly-elytron