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