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