WS-security (usernametoken) for CXF - encrypted passwords possible?

4.9k views Asked by At

I'm trying to get together with CXF's WS-security implementation(usernametoken). I've done everything as said at http://cxf.apache.org/docs/ws-security.html. My PasswordCallbackHandler seems to be working, but what bothers me is a part:

    if (pc.getIdentifier().equals("joe")) {
        // set the password on the callback. This will be compared to the
        // password which was sent from the client.
        pc.setPassword("password");
    }

as said

Note that for up to and including CXF 2.3.x, the password validation of the special case of a plain-text password (or any other yet unknown password type) is delegated to the callback class, see org.apache.ws.security.processor.UsernameTokenProcessor#handleUsernameToken() method javadoc of the WSS4J project. In that case, the ServerPasswordCallback should be something like the following one:

so up to cxf 2.3.x it was done like that

   if (pc.getIdentifer().equals("joe") {
       if (!pc.getPassword().equals("password")) {
            throw new IOException("wrong password");
       }
    }

My issue is: I don't want to pc.setPassword("plainTextPassword") as I want to store it in any resource. This up-to-2.3.x design would allow me to do this since I could encrypt it manually. Are there any ways of setting encrypted password in callback or doing usernametoken authentication for stored, encrypted passwords ?

I'm using cxf 2.5.x

2

There are 2 answers

0
muttonUp On

Callback Handlers are there to provide the plaintext password or verify a digest password where the plaintext password is known.

But if you don't know the plaintext i.e. its one way hashed, then the callback interface is not appropriate and you should create a class that implements the Validator interface.

Here is my example implementation of that interface that uses a JPA repository in which the password is already stored as a BCrypt hash.

Use with the ws-security.ut.validator property documented here

i.e. as a CXF property <entry key="ws-security.ut.validator" value-ref="com.package.CustomUsernameTokenValidator" />

public class CustomUsernameTokenValidator implements Validator {
    @Autowired
    ProfileRepository profileRepository;
    @Override
    public Credential validate(Credential credential, RequestData requestData) throws WSSecurityException {
        Profile profile = profileRepository.findByName(credential.getUsernametoken().getName());
        if (profile != null) {
            if (BCrypt.checkpw(credential.getUsernametoken().getPassword(), profile.getPassword())) {
                return credential;
            }
        }
        throw new WSSecurityException(WSSecurityException.ErrorCode.FAILED_AUTHENTICATION);     
    }
}
0
dmansfield On

The answer (which I've tried) is found in this blog page:

http://coheigea.blogspot.com/2011/06/custom-token-validation-in-apache-cxf.html

The essence is to create a subclass of org.apache.ws.security.validate.UsernameTokenValidator, and override the verifyPlaintextPassword method. In that method, the UsernameToken (which provides getName and getPassword) is passed. Throw an exception if they're not valid.

To install the custom validator in a spring configuration, add e.g.

  <jaxws:properties>
    <entry key="ws-security.ut.validator">
        <bean class="com.example.webservice.MyCustomUsernameTokenValidator" />
    </entry>
  </jaxws:properties>

into the <jaxws:endpoint/>.