You are viewing a plain text version of this content. The canonical link for it is here.
Posted to wss4j-dev@ws.apache.org by Sergio Patricio <se...@link.pt> on 2008/06/02 12:21:51 UTC

Encrypt soap body with UsernameToken

Hello,

I'm replacing a .Net web service by a Java web service using JAX-RPC.

Now I'm implementing security with WSS4J in the JAX-RPC handlers, I need to use the existing security model in the .Net web service (using WSE 3.0) so the clients have minimal changes.

 

The problem is that in WSE 3.0 it is possible to encrypt a soap body using a UsernameToken, also in the UsernameToken is possible to say not to send the password. An example of this is in http://www.codeproject.com/KB/XML/WSE30UsernameAssertion.aspx?display=Print

 

The old web service is using this technique to encrypt the message body, but in WSS4J I can't find a way to do it. I really don't know if it is possible to do it WSS4J.

So if it is possible how do I encrypt a soap body using a UsernameToken and add the UsernameToken to headers without the password?

 

Thanks

 

_____________________

Sérgio Patrício

sergio.patricio@link.pt

Link Consulting

 


RE: Encrypt soap body with UsernameToken

Posted by Sergio Patricio <se...@link.pt>.
Hello,

After many tries and combinations I think arranged a solution.

To resume, I want to sign and cipher soap messages using the UsernameToken and without certificates, the messages contains a UsernameToken, a Timestamp, a Signature (of timestamp and body) and the ciphered body.

I have to do a little hack in the messages because currently WSS4J doesn't allow me to send a UsernameToken without the password if I sign the message using this token, to don't send the password it has to be null but for signing can't be null, more information about this see JIRA WSS-127.

So basically in the element representing the UsernameToken I removed the password before the message is sent and when the messages arrives I put back the password before process the security header.

 

It's the first time I'm working with WSS4J so I can't guarantee that what I'm doing is correct.

The code is based on WSS4J handlers and test cases

 

Message sending:

Document doc = ...

 

WSSecHeader secHeader = new WSSecHeader();

secHeader.insertSecurityHeader(doc);

 

// add a timestamp - seconds

WSSecTimestamp secTimestamp = new WSSecTimestamp();

secTimestamp.setTimeToLive(300);

secTimestamp.prepare(doc);

 

// add usernametoken

WSSecUsernameToken secUsernameToken = new WSSecUsernameToken();

secUsernameToken.setUserInfo(user, password);

secUsernameToken.addNonce();

secUsernameToken.addCreated();

 

secUsernameToken.prepare(doc);

byte[] secretKey = secUsernameToken.getSecretKey();

 

// prepare signature

WSSecSignature secSignature = new WSSecSignature();

secSignature.setUsernameToken(secUsernameToken);

secSignature.setKeyIdentifierType(WSConstants.UT_SIGNING);

secSignature.setSignatureAlgorithm(XMLSignature.ALGO_ID_MAC_HMAC_SHA1);

 

secSignature.prepare(doc, null, secHeader);

 

// add tokens do header

secSignature.prependToHeader(secHeader);

secUsernameToken.prependToHeader(secHeader);

secTimestamp.prependToHeader(secHeader);

 

// compute signature 

SOAPConstants soapConstants = WSSecurityUtil.getSOAPConstants(doc.getDocumentElement());

 

Vector parts = new Vector();

// add timestamp

WSEncryptionPart timestampPart = new WSEncryptionPart(secTimestamp.getId());

parts.add(timestampPart);

 

// add body

WSEncryptionPart bodyPart = new WSEncryptionPart(soapConstants.getBodyQName().getLocalPart(), soapConstants.getEnvelopeURI(), "Content");

parts.add(bodyPart);

 

secSignature.addReferencesToSign(parts, secHeader);

secSignature.computeSignature();

 

 

// encrypt body

// create security token reference

Reference ref = new Reference(doc);

ref.setURI("#" + secUsernameToken.getId());

ref.setValueType("UsernameToken");

SecurityTokenReference secRef = new SecurityTokenReference(doc);

secRef.setReference(ref);

WSSecurityUtil.setNamespace(secRef.getElement(), WSConstants.WSSE_NS, WSConstants.WSSE_PREFIX);

 

WSSecEncrypt secEncrypt = new WSSecEncrypt();

secEncrypt.setKeyIdentifierType(WSConstants.EMBED_SECURITY_TOKEN_REF);

secEncrypt.setSecurityTokenReference(secRef);

secEncrypt.setKey(secretKey);

secEncrypt.setSymmetricEncAlgorithm(WSConstants.AES_256);

secEncrypt.setDocument(doc);

 

secEncrypt.build(doc, null, secHeader);

 

 

/* very important hack: remove password

 * 

 * The username password is used to build the secret key

 * can't be send in the message

 * Currently with WSS4J is not possible to sign using UsernameToken and

 * don't send the password (to don't send the password it has to be null) 

 */

boolean passwordRemoved = false;

NodeList nodeList = secUsernameToken.getUsernameTokenElement().getChildNodes();

Node node = null;

for (int i = 0; i < nodeList.getLength(); i++) {

    node = secUsernameToken.getUsernameTokenElement().getChildNodes().item(i);

    if (node.getLocalName() == "Password") {

        secUsernameToken.getUsernameTokenElement().removeChild(node);

        passwordRemoved = true;

        break;

    }

}

if (!passwordRemoved) {

    throw new JAXRPCException("Error removing UsernameToken password");

}

 

////////////////////////////////////////////////

Message receiving:

Document doc = ...

 

// Check if it's a fault. Don't process faults.

SOAPConstants soapConstants = WSSecurityUtil.getSOAPConstants(doc.getDocumentElement());

if (WSSecurityUtil.findElement(doc.getDocumentElement(), "Fault", soapConstants.getEnvelopeURI()) != null) {

    //don't process the message

    return;

}

 

// hack part 2: restore the removed password in the message          

UsernameToken usernameToken = null;

try {

    Element el = (Element) doc.getElementsByTagNameNS("*", "UsernameToken").item(0);

    Element elementPassword = doc.createElementNS(WSConstants.WSSE_NS, "wsse:" + WSConstants.PASSWORD_LN);

    WSSecurityUtil.setNamespace(elementPassword, WSConstants.WSSE_NS, WSConstants.WSSE_PREFIX);

    elementPassword.appendChild(doc.createTextNode(""));

    el.appendChild(elementPassword);

    

    usernameToken = new UsernameToken(el);

    usernameToken.setPassword(password);

} catch (Exception e) {

    //throw exception

    throw new JAXRPCException("Error rebuilding UsernameToken");

}

 

// process security header

WSSecurityEngine engine = new WSSecurityEngine();

Vector wsResult = engine.processSecurityHeader(doc, null, new PasswordCallbackHandler(usernameToken), null);

 

if (wsResult == null) {

    // no security header found

    throw new JAXRPCException("No security header found");

}

 

// set the header as processed

SOAPHeader sHeader = msg.getSOAPPart().getEnvelope().getHeader();

Iterator headers = sHeader.examineAllHeaderElements();

 

SOAPHeaderElement headerElement = null;

while (headers.hasNext()) {

    SOAPHeaderElement hE = (SOAPHeaderElement) headers.next();

    if (hE.getElementName().getLocalName().equals(WSConstants.WSSE_LN)

            && ((Node) hE).getNamespaceURI().equals(WSConstants.WSSE_NS)) {

        headerElement = hE;

        break;

    }

}

headerElement.setMustUnderstand(false);

 

 

/////////////////////////////////////////////////////////

And the password callback

public class PasswordCallbackHandler implements CallbackHandler {

    UsernameToken usernameToken = null;

    

    public PasswordCallbackHandler() {

    }

    

    public PasswordCallbackHandler(UsernameToken t) {

        usernameToken = t;

    }

 

    public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {

        for (int i = 0; i < callbacks.length; i++) {

            if (callbacks[i] instanceof WSPasswordCallback) {

                WSPasswordCallback pc = (WSPasswordCallback) callbacks[i];

                

                if (pc.getUsage() == WSPasswordCallback.USERNAME_TOKEN) {

                    pc.setPassword(password);

                } else if (pc.getUsage() == WSPasswordCallback.CUSTOM_TOKEN) {

                    pc.setKey(usernameToken.getSecretKey());

                }

            } else {

                throw new UnsupportedCallbackException(callbacks[i], "Unrecognized Callback");

            }

        }

    }

}

 

_____________________

Sérgio Patrício

sergio.patricio@link.pt

Link Consulting

 

From: Sergio Patricio [mailto:sergio.patricio@link.pt] 
Sent: segunda-feira, 2 de Junho de 2008 11:22
To: wss4j-dev@ws.apache.org
Subject: Encrypt soap body with UsernameToken 

 

Hello,

I'm replacing a .Net web service by a Java web service using JAX-RPC.

Now I'm implementing security with WSS4J in the JAX-RPC handlers, I need to use the existing security model in the .Net web service (using WSE 3.0) so the clients have minimal changes.

 

The problem is that in WSE 3.0 it is possible to encrypt a soap body using a UsernameToken, also in the UsernameToken is possible to say not to send the password. An example of this is in http://www.codeproject.com/KB/XML/WSE30UsernameAssertion.aspx?display=Print

 

The old web service is using this technique to encrypt the message body, but in WSS4J I can't find a way to do it. I really don't know if it is possible to do it WSS4J.

So if it is possible how do I encrypt a soap body using a UsernameToken and add the UsernameToken to headers without the password?

 

Thanks

 

_____________________

Sérgio Patrício

sergio.patricio@link.pt

Link Consulting