Question

I'm using Apache CXF to build a Web Service. It uses Apache WSS4J to provide WS-Security functionality. I need to make a SOAP request and it must be signed.

This is the content of the properties file I pass to WSS4J:

org.apache.ws.security.crypto.provider = org.apache.ws.security.components.crypto.Merlin
org.apache.ws.security.crypto.merlin.keystore.type = PKCS12
org.apache.ws.security.crypto.merlin.keystore.provider = BC
org.apache.ws.security.crypto.merlin.keystore.password = 12345678
org.apache.ws.security.crypto.merlin.keystore.alias = my-alias
org.apache.ws.security.crypto.merlin.keystore.file = my_certificate.p12

I want to get rid of that line with my password wrote as plain text. I removed that line and provided a password callback handler to my WSS4JOutInterceptor, like in the code above:

public SoapInterceptor newSignerInterceptor() {
    Map<String, Object> outProps = new HashMap<String, Object>();
    outProps.put(WSHandlerConstants.ACTION, "Signature");
    outProps.put(WSHandlerConstants.USER, config.getKeyAlias());
    outProps.put(WSHandlerConstants.SIG_KEY_ID, "DirectReference");
    outProps.put(WSHandlerConstants.USE_REQ_SIG_CERT, WSHandlerConstants.SIGNATURE_USER);
    outProps.put(WSHandlerConstants.USE_SINGLE_CERTIFICATE, "false");
    outProps.put(WSHandlerConstants.PW_CALLBACK_CLASS, this.getClass().getName());
    outProps.put(WSHandlerConstants.SIG_PROP_FILE, config.getPropertiesFileName());
    return new WSS4JOutInterceptor(outProps);

}

@Override
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
    for (int i = 0; i < callbacks.length; i++) {
        if (callbacks[i] instanceof WSPasswordCallback) {
            ((WSPasswordCallback) callbacks[i]).setPassword(password);
        }
    }
}

But that didn't work. It doesn't find the password in the properties file and uses a default password, "security".

How to make it use a callback to get the password?

Was it helpful?

Solution

You can implement a CallbackHandler:

public class PasswordCallbackHandler implements CallbackHandler {

    @Override
    public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
        for(Callback callBack:callbacks){
            if(callBack instanceof WSPasswordCallback){
                ((WSPasswordCallback)callBack).setPassword("password");
            }
        }
    }
}

then add the handler to the properties:

outProps.put(WSHandlerConstants.PW_CALLBACK_CLASS, PasswordCallbackHandler.class);

you can also use PW_CALLBACK_REF to set a reference of the handler.

OTHER TIPS

Merlin doesn't invoke the callback for Keystore password, so the password always has to be in the property file. Fortunately it can be encrypted.

The solution is described nicely here: Encrypting passwords in Crypto property files

Copied solution from the link above:

  1. Download the jasypt-1.9.2-dist.zip
  2. Get a Encoded password with this command encrypt input=real_keystore_password password=master_password algorithm=PBEWithMD5AndTripleDES
  3. Copy the OUTPUT (EXample: 0laAaRahTQJzlsDu771tYi)
  4. As you're using this algorithm, you need the Java Cryptography Extension (JCE) Unlimited Strength. Put in your JDK.
  5. Put the encoded output in the properties

    org.apache.wss4j.crypto.provider=org.apache.wss4j.common.crypto.Merlin
    
    org.apache.wss4j.crypto.merlin.keystore.type=jks 
    org.apache.wss4j.crypto.merlin.keystore.password=ENC(0laAaRahTQJzlsDu771tYi)
    
    org.apache.wss4j.crypto.merlin.keystore.alias=my_alias 
    org.apache.wss4j.crypto.merlin.keystore.file=/etc/cert/my_keystore.jks
    
  6. In the CallbackHandler, put the master_password wich you used to generated the encoded one:

    public class WsPasswordHandler implements CallbackHandler {
    @Override
    public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
    
        for (Callback callback: callbacks){
            WSPasswordCallback pwdCallback= (WSPasswordCallback) callback;
            final int usage =pwdCallback.getUsage();
            if (usage == WSPasswordCallback.SIGNATURE || usage==WSPasswordCallback.DECRYPT) {
            pwdCallback.setPassword("parKeyPassword");
        }
        if (usage==WSPasswordCallback.PASSWORD_ENCRYPTOR_PASSWORD){
            pwdCallback.setPassword("master_password");
        }
        }           
    }
    

    }

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top