Wednesday, 23 December 2015

WSSE PasswordDigest

Update: See latest blog post on this.

Posting for posterity - as it was driving me nuts. Next person stuck on this may find this post!

Trying to sort the API to Royal Mail right now. They use SOAP headers for XML messages posted via https. This is very common and very easy to put together if you have an XML library.

Now, apart from the fact it is all over https, and they want us to use a client certificate even, the XML that is sent has a system for authentication in the SOAP header which uses a UsernameToken

This contains a Username and could just contain a Password. We are using a secure channel so why not. But no, it has more. You can send, instead, a PasswordDigest which is a hash.

Sensibly, to avoid replay attacks and make the password itself hidden, the hash used is not just a simple hash of the password (that would be too easy, though it is an option). What they do is send a Nonce and a Created timestamp, and then use these with the password to make a PasswordDigest.

The timestamp allows for Nonce retention for a short tim for replay controls. The random data of the Nonce ensures the hash is not the same and cannot be rainbow table attacked for the password. So sensible enough if you were using a less secure channel.

The specification explains the digest used. It even has an example. But like every other example I have seen so far it fails to state the password that applies so that someone can actually work through the example and confirm they have the code right.

What is worse is the Royal Mail complicate the password they use which is not the password they tell you but a hash of it, sort of.

So, this is specific to Royal Mail ApiAPI, but I expect if general to WSSE Password Digests.

The spec says:-

Password_Digest = Base64 ( SHA-1 ( nonce + created + password ) )

Base64 of SHA-1 is simple, so what is the data you pass. Password seems simple, it is the password. Created, well, that is the actual text of the Created field, so an XML style date/time. The fact that it is that exact text seems not 100% clear, but it is. They go to the bother of saying it is the UTF-8 text of the date/time which seems odd as a date time surely can never contain any non simple printable ASCII characters. The Password is also UTF-8 which is worth specifying.

But what of the Nonce? Well, that is, for Royal Mail, a 16 byte random number, encoded when sent using Base64. It was very unclear if the SHA-1 used the text Nonce as sent (i.e. the Base64 text) or the raw 16 byte value. I have seen several code fragments. I think it depends on how the Nonce is encoded, and if it specifically states it is Base64 encoded in the data structure (e.g. EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary") then you use the underlying 16 byte binary value in the SHA-1. That is what is needed for the Royal Mail API.

That sounds fine, but the Royal Mail do not do quote that. They state "For Shipping API V2 the password used in the below formula is the base 64 encoding of the SHA-1 hash of the plain text password". OK, so instead of "password" you use the base64 of the SHA-1. They then state the digest is:-

Password_Digest = Base64(SHA-1(Nonce + Created + SHA-1(Password)))

Well, that is simple enough. Take password, SHA-1 it, Base64 it, then use that in above, so SHA-1 that, then append on to Nonce and Created and SHA-1 that and base64 it. Well no!!!

What them mean actually is, where Password is plain text, use :-

Password_Digest = Base64(SHA-1(Nonce + Created + Base64(SHA-1(Password))))

And that matches their examples!

Update: It matches the example, but is apparently wrong. They have sent a new example where the password is just the SHA-1(plaintextpassword) not the Base64(SHA-1(plaintextpassword)), see later post...

Update: Finally working on a test application ID, and using this crazy base64(SHA-1(Password)) logic as I originally documented here. Not working on the actual on boarding system though.

In this example the password is "password".

<UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="55555555555555555555555555555555">
            <Username>FredBloggs</Username>
            <Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">Z+VcU4ICFQReHwG/Kt70jhJgtaM=</Password>
            <Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">VVVVVVVVVVVVVVVVVVVVVQ==</Nonce>
            <Created>2015-12-23T18:50:29Z</Created>
</UsernameToken>
  • SHA-1 of "password" is 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8 hex
  • Base64 of that is W6ph5Mm5Pz8GgiULbPgzG37mj9g=
  • Nonce is VVVVVVVVVVVVVVVVVVVVVQ== in base 64
  • Which is 55555555555555555555555555555555 hex
  • Which is UUUUUUUUUUUUUUUU in text
  • Created is 2015-12-23T18:50:29Z
  • SHA-1 of UUUUUUUUUUUUUUUU2015-12-23T18:50:29ZW6ph5Mm5Pz8GgiULbPgzG37mj9g= is 67e55c53820215045e1f01bf2adef48e1260b5a3 hex
  • Which, in base64 is Z+VcU4ICFQReHwG/Kt70jhJgtaM=

No comments:

Post a Comment