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?
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.
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:
encrypt input=real_keystore_password password=master_password algorithm=PBEWithMD5AndTripleDES
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
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"); } } }
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.