Implementing password digest for ws-security UsernameToken in Java

Stian Sigvartsen picture Stian Sigvartsen · Jun 9, 2012 · Viewed 17.1k times · Source

I am trying to make a call to a ws-security secured webservice from a server which unfortunately does not support this natively. The approach I have taken is to implement a .jsp which acts as reverse proxy to the actual end point URL, in the process adding the element with ws-security elements.

This seems to be working quite well and I am confident I've constructed the XML correctly with the correct namespaces etc. I've verified this by comparing the XML with XML produced by SOAP-UI.

The problem is in implementing the password digest generator. I don't get the same result as what SOAP-UI does using the same inputs for NOnce, xsd:dateTime and password, and the following code.

StringBuffer passwordDigestStr_ = new StringBuffer();

// First append the NOnce from the SOAP header
passwordDigestStr_.append(Base64.decode("PzlbwtWRpmFWjG0JRIRn7A=="));

// Then append the xsd:dateTime in UTC timezone
passwordDigestStr_.append("2012-06-09T18:41:03.640Z");

// Finally append the password/secret
passwordDigestStr_.append("password");

System.out.println("Generated password digest: " + new String(com.bea.xbean.util.Base64.encode(org.apache.commons.codec.digest.DigestUtils.sha(passwordDigestStr_.toString())), "UTF-8"));

I think the problem is with implementing the hashing of the first two elements as explained by http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0.pdf

Note that the nonce is hashed using the octet sequence of its decoded value while the timestamp is hashed using the octet sequence of its UTF8 encoding as specified in the contents of the element.

If anyone could help me solve this problem that would be great because it's beginning to drive me crazy! It would be ideal if you could provide source code.

Answer

John Watts picture John Watts · Jun 10, 2012

I'll take a crack at it without SOAP-UI. The input to the hash function is supposed to be bytes, not a string. DigestUtils.sha() will allow you to use a string, but that string must be properly encoded. When you wrote the nonce, you were calling StringBuffer.append(Object) which ends up calling byte[].toString(). That gives you something like [B@3e25a5, definitely not what you want. By using bytes everywhere, you should avoid this problem. Note that the example below uses org.apache.commons.codec.binary.Base64, not the Base64 class you were using. It doesn't matter, that's just the one I had handy.

ByteBuffer buf = ByteBuffer.allocate(1000);
buf.put(Base64.decodeBase64("PzlbwtWRpmFWjG0JRIRn7A=="));
buf.put("2012-06-09T18:41:03.640Z".getBytes("UTF-8"));
buf.put("password".getBytes("UTF-8"));
byte[] toHash = new byte[buf.position()];
buf.rewind();
buf.get(toHash);
byte[] hash = DigestUtils.sha(toHash);
System.out.println("Generated password digest: " + Base64.encodeBase64String(hash));