Encrypt password in wildfly-config.xml

1,348 views
Skip to first unread message

Arkady Zelekman

unread,
Apr 19, 2021, 10:33:53 AM4/19/21
to WildFly
I am using Wildfly 23.0.0, having following wildfly-config.xml on client application:

<configuration>
    <authentication-client xmlns="urn:elytron:1.0">
        <authentication-rules>
                    <rule use-configuration="default" />
        </authentication-rules>
        <authentication-configurations>
            <configuration name="default">
                <sasl-mechanism-selector selector="#ALL" />
                <set-mechanism-properties>
                    <property key="wildfly.sasl.local-user.quiet-auth" value="true" />
                 </set-mechanism-properties>
                <providers>
                    <use-service-loader/>
                </providers>
                <!-- Used for EJB over HTTP, remoting invocations will use transparent auth-->
                <set-user-name name="system" />
                <credentials>
                    <clear-password password="system" />
                </credentials>
             </configuration>
        </authentication-configurations>
    </authentication-client>
</configuration>

We would like to get rid of having clear password here. I found I can use "masked-password" instead of "clear-password" but didn't find any tutorial with a real example how to do this.

Can anyone help ?

dvilkola

unread,
Apr 19, 2021, 11:57:36 AM4/19/21
to WildFly

Arkady Zelekman

unread,
Apr 20, 2021, 12:20:44 AM4/20/21
to WildFly
I saw this blog, but their program example is not compiled, marked in red:

MaskedPasswordAlgorithmSpec maskedAlgorithmSpec = new MaskedPasswordAlgorithmSpec(key, iterationCount, salt);
...
MaskedPasswordSpec maskedPasswordSpec = new MaskedPasswordSpec(keyMaterial, iterationCount, salt, masked);

May some working example exists everywhere ?

Tomas Andersson

unread,
Mar 3, 2022, 1:26:42 PM3/3/22
to WildFly
Did you find a solution? I also found that blog post and noticed the compilation errors and managed to sort them out
key should be keyMaterial
masked should be original.getMaskedPasswordBytes()

However I still do not get the actual usage of the MaskedPassword to work in the programmatic approach the below code gives an exception: AuthenticationConfiguration maskedConfig = AuthenticationConfiguration.empty().useMaskedPassword("/Nym2s/dssMrabfdIGsZfQ==", null, null, 100, "12345678", null);

java.security.NoSuchAlgorithmException: ELY08028: Invalid algorithm "masked-MD5-DES"

This is because the underlying code does: 
PasswordFactory factory = PasswordFactory.getInstance(algorithm);
without passing WildFlyElytronProvider(that is Deprecated anyway) as the blogger did when creating the MaskedPassword.

Noticed that there is a AuthenticationConfiguration.useProviders() and tried to put in the WildFlyElytronProvider but did not get that to work either.

Super weird that such a trivial use case can be so hard to get to work. The only thing I want to do is make the user enter their AD username and password and then pass that in some encrypted manner to the remote secure bean to be picked up by a distributed-realm and used for authenticating and get the users roles from either an ldap-realm or even the default ApplicationRealm. Works great if I send the credential in clear text but why would I want to do that over a network(even if it is not Internet-facing)?

Tomas Andersson

unread,
Mar 4, 2022, 1:22:06 AM3/4/22
to WildFly
Ok, so I am lost here :-) Seems like the useMaskedPassword(MaskedPassword password) converts the MaskedPassword back to cleartext before setting it in the configuration which would mean that it is sent in clear text anyway? I was hoping that the client would send the MaskedPassword and that I could configure server side how to decrypt it to avoid sending the clear text password. Could someone please enlighten me?

Tomas Andersson

unread,
Mar 4, 2022, 2:32:27 AM3/4/22
to WildFly
I know I can send the MaskedPassword version or some other encrypted version in the password field but how would I get for example the ApplicationRealm to decrypt it before validating it? If I send in the password in its DIGEST-MD5 form the ApplicationRealm deems it invalid(and if sniffed it isn´t very hard to recover the clear text version anyway). One of the realms we use in our distributed-realm is an ldap and that is supposed to be able to handle DIGEST-MD5 but so far I have not gotten that to work either.

I am obviously missing something major here :-)

Darran Lofthouse

unread,
Mar 4, 2022, 3:48:10 AM3/4/22
to WildFly
Sending the masked password in the clear would make it the equivalent of a clear text password - the moment anyone had access to the masked password they would be able to use it in exactly the same way as a clear text password so it would not add any protection.

The three areas to consider are:
  1. Use a credential store with the wildfly-config.xml to move the credential from the configuration to a credential store.
  2. Make use of authentication mechanisms such as DIGEST or SCRAM, these are a challenge response type mechanism which allow the client to prove they know the password without sending it across the network.
  3. Activate TLS to ensure confidentiality of the communication.

Tomas Andersson

unread,
Mar 4, 2022, 6:48:21 AM3/4/22
to WildFly
Thanks for the response, yes I agree totally! I am just trying to find anything that can get me closer to solving my problem.

The only thing I want to do is make the user enter their AD username and password and then pass that in some encrypted manner to the remote secure bean to be picked up by a distributed-realm and used for authenticating and get the users roles from either an ldap-realm or in some cases another realm. Works great if I send the credential in clear text but I first tried DIGEST but could not get it to work. 

I haven't found any examples of how to programmatically encrypt the user supplied password in the client, add it to the AuthenticationConfiguration and get an ldap-realm to decrypt and verify it.

So I am blindly experimenting back and forth just using the wildfly quickstarts and elytron-examples debugging elytron etc but at the moment I just try to use the quickstart/ejb-security and create the AuthenticationContext like this:

PasswordFactory passwordFactory = PasswordFactory.getInstance("digest-md5", new WildFlyElytronPasswordProvider());
Credential credential = new PasswordCredential(new PasswordCredential(passwordFactory.generatePassword(new DigestPasswordSpec("quickstartUser", "ApplicationRealm", "quickstartPwd1!".getBytes()))).getPassword());
final NamePrincipal principal = new NamePrincipal("leanonreader");
AuthenticationConfiguration ejbConfig = AuthenticationConfiguration.empty()
    .useName("quickstartUser")
    .useCredential(credential)
    //.usePassword("quickstartPwd1!")
    .useRealm("ApplicationRealm")
    .setSaslMechanismSelector(SaslMechanismSelector.fromString("DIGEST-MD5"));

AuthenticationContext authenticationContext = AuthenticationContext.empty().with(MatchRule.ALL.matchHost("localhost"), ejbConfig);

But that does not work, if I switch to the clear text password it works though.

Tomas Andersson

unread,
Mar 4, 2022, 6:55:58 AM3/4/22
to WildFly
digest – the digest: H( username ":" realm ":" password )
what does the H stand for?
tried "quickstartUser:ApplicationRealm:quickstartPwd1!" as well.


Tomas Andersson

unread,
Mar 4, 2022, 7:29:06 AM3/4/22
to WildFly
Ah,

PasswordFactory passwordFactory = PasswordFactory.getInstance(DigestPassword.ALGORITHM_DIGEST_MD5, ELYTRON_PROVIDER);

DigestPasswordAlgorithmSpec digestAlgorithmSpec = new DigestPasswordAlgorithmSpec("quickstartUser", "ApplicationRealm");
EncryptablePasswordSpec encryptableSpec = new EncryptablePasswordSpec("quickstartPwd1!".toCharArray(), digestAlgorithmSpec);

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

byte[] digest = original.getDigest();

Credential credential = new PasswordCredential(new PasswordCredential(passwordFactory.generatePassword(new DigestPasswordSpec("quickstartUser", "ApplicationRealm", digest))).getPassword());

Tomas Andersson

unread,
Mar 4, 2022, 1:52:35 PM3/4/22
to WildFly
@Darran 

So after getting the digest-md5 to work in ejb-security I again tried it in my project code and if I only use ApplicationRealm there it works but when using it as part of a distributed-realm it did not work. I probably do not need a distributed-realm but the main issue that I started with was how to use the user provided password encrypted with the ldap-realm and that I still cannot get to work. Here is the config from my standalone.xml:

<sasl-authentication-factory name="application-sasl-authentication-ldap" sasl-server-factory="configured" security-domain="ApplicationDomain">
    <mechanism-configuration>
        <mechanism mechanism-name="DIGEST-MD5">
            <mechanism-realm realm-name="CachedLdapRealm"/>
        </mechanism>
        <mechanism mechanism-name="PLAIN">
            <mechanism-realm realm-name="CachedLdapRealm"/>
        </mechanism>
    </mechanism-configuration>
</sasl-authentication-factory>

<security-domain name="ApplicationDomain" default-realm="CachedLdapRealm" permission-mapper="default-permission-mapper">
    <realm name="CachedLdapRealm" role-decoder="from-roles-attribute"/>
</security-domain>

<ldap-realm name="LdapRealm" dir-context="ldap-connection" direct-verification="true">
   <identity-mapping rdn-identifier="${env.AD_IDENTIFIER}" search-base-dn="${env.AD_SEARCH_BASE}">
        <attribute-mapping>
            <attribute from="${env.AD_ROLE_ATTRIBUTE}" to="Roles" filter="(${env.AD_IDENTIFIER}={0})" filter-base-dn="${env.AD_FILTER_BASE}"/>
        </attribute-mapping>
    </identity-mapping>
</ldap-realm>
<caching-realm name="CachedLdapRealm" realm="LdapRealm"/>

<dir-contexts>
    <dir-context name="ldap-connection" url="${env.AD_HOST}" principal="CN=${env.AD_PRINCIPAL}, ${env.AD_SEARCH_BASE}">
        <credential-reference clear-text="${env.AD_CREDENTIAL}"/>
    </dir-context>
</dir-contexts>

Config that work in the client:
ejbConfig = AuthenticationConfiguration.empty()
    .usePassword(password)
    .usePrincipal(principal)
    .useAuthorizationName(userName)
    .useAuthorizationPrincipal(principal)
    .setSaslMechanismSelector(SaslMechanismSelector.fromString("PLAIN"))
    .useRealm(realm);

Not working:
ejbConfig = AuthenticationConfiguration.empty()
    .useCredential(credential)
    .usePrincipal(principal)
    .useAuthorizationName(userName)
    .useAuthorizationPrincipal(principal)
    .setSaslMechanismSelector(SaslMechanismSelector.fromString("DIGEST-MD5"))
    .useRealm(realm);

I get:

2022-03-04 19:50:59,818 TRACE [org.wildfly.security] (default task-1) Handling PasswordCallback: PasswordCredential may not be supported
2022-03-04 19:50:59,818 TRACE [org.wildfly.security.sasl.digest] (default task-1) SASL Negotiation Failed
2022-03-04 19:50:59,818 TRACE [org.jboss.remoting.remote.server] (default task-1) Server sending authentication rejected: javax.security.sasl.SaslException: ELY05051: Callback handler does not support credential acquisition [Caused by org.wildfly.security.auth.callback.FastUnsupportedCallbackException: javax.security.auth.callback.PasswordCallback@5b87dd4]

Any ideas?

/Tomas

Darran Lofthouse

unread,
Mar 4, 2022, 2:16:06 PM3/4/22
to WildFly
On the LDAP realm as you are using direct-verification it means the realm does require the clear text password of the user to try and establish a connection to the LDAP server as that user to verify that the provided password is valid.

To use Digest authentication from LDAP the configuration would need to extract the users password from LDAP for verification rather than attempting to use the users password directly.

Tomas Andersson

unread,
Mar 4, 2022, 3:35:10 PM3/4/22
to WildFly
I see, but that didn't really work either as discussed in another thread(https://groups.google.com/u/1/g/wildfly/c/8f5AAcoU7HY) because I had to add a "service" user in the dir-context to bind to the connection before searching and verifying the user provided credentials, the below does not work: 
<dir-contexts>
    <dir-context name="ldap-connection" url="${env.AD_HOST}" />
</dir-contexts>
When using PLAIN the ldap-realm binds with the service user but then verify the users username and credential and I can fetch the users roles. But I do not want to send the credentials that our users provide in clear text, it is one thing that the service users is sent in clear text which is bad enough.

Tomas Andersson

unread,
Mar 4, 2022, 5:32:32 PM3/4/22
to WildFly
Thanks Darran for trying to clarify!

 I thought this would be easy but haven't really found any docs or tutorials that cover our case but perhaps that say more about us :-)

You mentioned:

"To use Digest authentication from LDAP the configuration would need to extract the users password from LDAP for verification rather than attempting to use the users password directly." 

Can that be achieved all through the ldap-realm configuration or would that require auth to the bean using the service user and retrieving the user credentials and then do the verification in our own code?

That we already kind of have, what I was hoping for was to have a one stop shop with direct-verification(without the need for a "service" user) with the passwords sent encrypted all the way (given that we could send it in a way ldap could decrypt and verify) and also have the caching-realm to minimize the number of calls to AD.

A bonus would be if the caching-realm would provide a sessionId that could be used for subsequent calls to the ejbs so that we did not need to store the users password at all in the client :-)

/Tomas

Farah Juma

unread,
Mar 7, 2022, 2:29:29 PM3/7/22
to WildFly
On Friday, March 4, 2022 at 5:32:32 PM UTC-5 Tomas Andersson wrote:
Thanks Darran for trying to clarify!

 I thought this would be easy but haven't really found any docs or tutorials that cover our case but perhaps that say more about us :-)

You mentioned:

"To use Digest authentication from LDAP the configuration would need to extract the users password from LDAP for verification rather than attempting to use the users password directly." 

Can that be achieved all through the ldap-realm configuration or would that require auth to the bean using the service user and retrieving the user credentials and then do the verification in our own code?

There's some documentation related to this in Section 4.1.4 here:


As described in that section, it's possible to configure an LDAP realm that contains hashed passwords using the hash-encoding attribute to indicate the format of the hashed password (e.g., hex or base64, the default is base64). 

In case it helps, this test class has some examples on what the password stored in the LDIF file would like in different cases:


 

Tomas Andersson

unread,
Mar 8, 2022, 5:06:28 AM3/8/22
to WildFly
Thanks Farah, I have seen that also but not sure if it really make our setup any more secure though? Basically the step I am most concerned about is having to send the users password in clear text from the client by setting it with:
AuthenticationConfiguration.empty().usePassword(password) 
that is used in the AuthenticationContext used to call the remote secure bean that is secured through the cached-realm with an underlying ldap-realm. 

Even though that call is made through https this would add a need to store the password in the client somehow to be used for each subsequent call to the secured ejbs as the ldap-realm functionality apparently need it in clear text(at least for direct verification). 

I am not overly concerned about the communication between the server and the ldap server though as that is pretty secure in our case but I would like to be able to encrypt the password provided by the user in the client and send the encrypted version to the server and then if elytron need to decrypt it(through some configured agreement between the client and the server) to be used for the authentication and authorization against ldap I am fine with that(even if I'd prefer if that also could be handled encrypted somehow).

Would be really nice if the cached-realm would accept an encrypted password and store that in the cache instead of the clear text one(perhaps it does I just don't know how it it is implemented) and provide the client with some kind of session id that could be stored in the client and used for subsequent calls. 
If not possible, can I somehow hook that functionality in myself in the server by extending some class or add some filter/configuration kind of like in Spring?

Another question is if the elytron cached-realm has any eviction policy that can be set to require the user to reenter the credential after some timeout or if the cached credentials remain there until server restart?

Tomas Andersson

unread,
Mar 8, 2022, 5:29:05 AM3/8/22
to WildFly
Correction to last question:
Another question is if the elytron cached-realm has any eviction policy that can be set to require the ldap-realm to verify the credentials at the ldap after some timeout or if the cached credentials remain there until server restart?

I know it is possible to create a custom elytron realm that perhaps could do what I am after but I'd prefer some standardized alternative that we do not need to maintain ourselves :-)

Tomas Andersson

unread,
Mar 11, 2022, 3:12:04 AM3/11/22
to WildFly
@Darran 
Ok, so I have experimented with implementing a custom-realm replacing both the cached-realm and the ldap-realm to be able to handle the flow the way we want to but still can not get it to the work.

Is there any way in Elytron that I can get an encrypted password set in the client down to my custom-realm either as some sort of encrypted PasswordGuessEvidence or just as the original Credential sent in?

Reply all
Reply to author
Forward
0 new messages