How to make WSS4J load the keystore password from a callback?
Asked Answered
V

2

7

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?

Virchow answered 15/4, 2013 at 22:20 Comment(0)
S
7

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.

Soubise answered 8/7, 2014 at 23:40 Comment(0)
M
0

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");
        }
        }           
    }
    

    }

Maiamaiah answered 20/2, 2018 at 17:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.