I am consuming a web service which requires wsse security in the header of the soap requests and consists of the created, nonce and password digest. The web server uses these values to authorise a genuine request.
The password digest is created using the following algorithm as stated in the API of the web service:
Password Digest The OASIS Usertoken profile defines and describes the formula that computes the unique Password_Digest string submitted in the XML For Shipping API the password information used in this formula is the base 64 encoding of the SHA-1 hash of the plain text password.
The formula to use to construct the Password_Digest value is
Password_Digest = Base64(SHA-1(Nonce + Created + Base64(SHA-1(Password))))
Note the + symbol in the above algorithm represents a string concatenation of the three strings: Nonce from xml request, Created from xml request and Base64 encoding of the SHA-1 digest of the Password.
I am working with a sample XML request which is known to have successfully been authorised. The problem I'm running into is I am getting a different value for the password digest when I try to recreate it with the values provided in the sample.
The sample values and expected password_digest:
Nonce: 4ETItj7Xc6+9sEDT5p2UjA==
Created: 2014-08-04T10:22:48.994Z
Password: Password2014!
Password_Digest: Ug3FRXgyAaWU8SjYHRabnAkn330=
Here's the output of various methods i tried when trying to recreate the password_digest
string nonce = "4ETItj7Xc6+9sEDT5p2UjA==";
string created = "2014-08-04T10:22:48.994Z";
string password = "Password2014!";
Method 1
string passwordDigest = TestCall.encodeBase64SHA1(nonce + created + TestCall.encodeBase64SHA1(password));
private static string encodeBase64SHA1(string phrase)
{
UTF8Encoding encoder = new UTF8Encoding();
SHA1CryptoServiceProvider sha1Hasher = new SHA1CryptoServiceProvider();
byte[] hashedDataBytes = sha1Hasher.ComputeHash(encoder.GetBytes(phrase));
return Convert.ToBase64String(hashedDataBytes);
}
//passwordDigest = z5nyI25z7CBUL7l7UkTH8E8azlQ=
Method 2
string passwordDigest = TestCall.encodeBase64SHA1Managed(nonce + created + TestCall.encodeBase64SHA1Managed(password));
private static string encodeBase64SHA1Managed(string phrase)
{
using (SHA1Managed sha1 = new SHA1Managed())
{
byte[] hash = sha1.ComputeHash(Encoding.UTF8.GetBytes(phrase));
return Convert.ToBase64String(hash);
}
}
//passwordDigest = z5nyI25z7CBUL7l7UkTH8E8azlQ=
Method 3
string passwordDigest = TestCall.encodeAsciiToBase64SHA1(nonce + created + TestCall.encodeAsciiToBase64SHA1(password));
private static string encodeAsciiToBase64SHA1(string phrase)
{
ASCIIEncoding encoder = new ASCIIEncoding();
SHA1CryptoServiceProvider sha1Hasher = new SHA1CryptoServiceProvider();
byte[] hashedDataBytes = sha1Hasher.ComputeHash(encoder.GetBytes(phrase));
return Convert.ToBase64String(hashedDataBytes);
}
//passwordDigest = z5nyI25z7CBUL7l7UkTH8E8azlQ=
Method 4
string passwordDigest = TestCall.encodeBase64SHA1HexString(nonce + created + TestCall.encodeBase64SHA1HexString(password));
private static string encodeBase64SHA1HexString(string phrase)
{
UTF8Encoding encoder = new UTF8Encoding();
SHA1CryptoServiceProvider sha1Hasher = new SHA1CryptoServiceProvider();
byte[] hashedDataBytes = sha1Hasher.ComputeHash(encoder.GetBytes(phrase));
StringBuilder output = new StringBuilder();
for (int i = 0; i < hashedDataBytes.Length; i++)
{
output.Append(hashedDataBytes[i].ToString("X2"));
}
return Convert.ToBase64String(Encoding.UTF8.GetBytes(output.ToString()));
}
//passwordDigest =RjE4REQyRDg5MjFGMkZCNTM2MTMwOEM1MTkzRDc1RTZCNDgwMjhCNQ==
None of the methods created the password_digest that matched the sample. Methods 1 - 3 at least created the password_digest which had the same number of characters as the sample so am I correct in assuming that the encoding is partially correct in the sense that the end number of bytes of the strings match?
My question is can anyone help to recreate the password_digest with the sample values provided?
I'd just like to mention this is the first time I've worked with ws-security and have little exposure to SHA-1 and string encoding/decoding so any help would be much appreciated.
Note:
The nonce string in the sample ends with '==' suggesting it has been encoded some how and the xml schema for the Nonce element is as follows:
<wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">4ETItj7Xc6+9sEDT5p2UjA==</wsse:Nonce>
Which suggests the nonce is encoded to base64 so i tried to rerun the methods again but with the nonce converted to plain text with the following line
nonce = Encoding.UTF8.GetString(Convert.FromBase64String(nonce));
//nonce = �Dȶ>�s���@�杔�
From the result i don't think the API company would create a nonce like this or my assumptions are wrong about the nonce being encrypted or I'm not decoding it correctly.
I still tried with the new value for the nonce and got the following results
Method 1, 2
//passwordDigest = WcuTBY2W06vv2/JemRuorgxMCns=
Method 3
//passwordDigest = KPHT7/ojTkvI6kJCaojbp0wKFZ4=
Method 4
//passwordDigest = NzRBOTM2NUQ2RjAyMjEzN0E1NEVCN0Q0NEExODU2M0U4Q0FEMDkyQg==
So again no success and the result of method 3 differs to method 1 and 2 which was unexpected as with the previous nonce methods 1, 2 and 3 gave the same results.