我正在使用一个Web服务,该服务在soap请求的标头中需要wsse安全性,并包含已创建的nonce和密码摘要。 Web服务器使用这些值来授权真实请求。
密码摘要是使用Web服务API中所述的以下算法创建的:
密码摘要 OASIS Usertoken概要文件定义并描述了计算XML中提交的唯一Password_Digest字符串的公式 对于Shipping API,此公式中使用的密码信息是纯文本密码的SHA-1哈希的基本64编码。
用于构造Password_Digest值的公式是
Password_Digest = Base64(SHA-1(Nonce + Created + Base64(SHA-1(密码))))
注意上面算法中的+符号表示三个字符串的字符串连接:来自xml请求的Nonce,来自xml请求的创建和密码的SHA-1摘要的Base64编码。
我正在使用已知已成功获得授权的示例XML请求。我遇到的问题是,当我尝试使用示例中提供的值重新创建密码摘要时,我获得了不同的值。
示例值和预期的password_digest:
Nonce :4ETItj7Xc6 + 9sEDT5p2UjA ==
创建:2014-08-04T10:22:48.994Z
密码:Password2014!
Password_Digest :Ug3FRXgyAaWU8SjYHRabnAkn330 =
这是我尝试重新创建password_digest时尝试的各种方法的输出
string nonce = "4ETItj7Xc6+9sEDT5p2UjA==";
string created = "2014-08-04T10:22:48.994Z";
string password = "Password2014!";
方法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=
方法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=
方法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=
方法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==
没有一种方法创建了与样本匹配的password_digest。方法1 - 3至少创建了与样本具有相同字符数的password_digest,所以我在假设编码部分正确的意义上是正确的,因为字符串的结束字节数匹配?
我的问题是,任何人都可以帮助重新创建带有提供的样本值的password_digest吗?
我想提一下这是我第一次使用ws-security并且很少接触SHA-1和字符串编码/解码,所以任何帮助都会非常感激。
注意:
示例中的nonce字符串以&#39; ==&#39;结尾。建议它已编码一些如何和Nonce元素的xml架构如下:
<wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">4ETItj7Xc6+9sEDT5p2UjA==</wsse:Nonce>
这表明nonce被编码为base64所以我试图再次重新运行这些方法,但是使用以下行将nonce转换为纯文本
nonce = Encoding.UTF8.GetString(Convert.FromBase64String(nonce));
//nonce = �Dȶ>�s���@�杔�
从结果中我不认为API公司会创建这样的nonce,或者我的假设是关于nonce被加密或我没有正确解码它的错误。
我仍尝试使用nonce的新值并得到以下结果
方法1,2
//passwordDigest = WcuTBY2W06vv2/JemRuorgxMCns=
方法3
//passwordDigest = KPHT7/ojTkvI6kJCaojbp0wKFZ4=
方法4
//passwordDigest = NzRBOTM2NUQ2RjAyMjEzN0E1NEVCN0Q0NEExODU2M0U4Q0FEMDkyQg==
所以再次没有成功,方法3的结果与方法1和2不同,这是出乎意料的,因为之前的nonce方法1,2和3给出了相同的结果。
答案 0 :(得分:2)
我玩示例和公式,然后我发现nounce需要先解码成字节然后应用公式
这是完整的例子,结果正确:
class Program
{
static void Main(string[] args)
{
// Base64(SHA-1(Nonce + Created + Base64(SHA-1(Password))))
string nonce = "4ETItj7Xc6+9sEDT5p2UjA==";
string createdString = "2014-08-04T10:22:48.994Z";
string password = "Password2014!";
string basedPassword = System.Convert.ToBase64String(SHAOneHash(Encoding.UTF8.GetBytes(password)));
byte[] combined = buildBytes(nonce, createdString, basedPassword);
string output = System.Convert.ToBase64String(SHAOneHash(combined));
Console.WriteLine("result is: " + output); // Ug3FRXgyAaWU8SjYHRabnAkn330=
Console.ReadKey();
}
private static byte[] buildBytes(string nonce, string createdString, string basedPassword)
{
byte[] nonceBytes = System.Convert.FromBase64String(nonce);
byte[] time = Encoding.UTF8.GetBytes(createdString);
byte[] pwd = Encoding.UTF8.GetBytes(basedPassword);
byte[] operand = new byte[nonceBytes.Length + time.Length + pwd.Length];
Array.Copy(nonceBytes, operand, nonceBytes.Length);
Array.Copy(time, 0, operand, nonceBytes.Length, time.Length);
Array.Copy(pwd, 0, operand, nonceBytes.Length + time.Length, pwd.Length);
return operand;
}
public static byte[] SHAOneHash(byte[] data)
{
using (SHA1Managed sha1 = new SHA1Managed())
{
var hash = sha1.ComputeHash(data);
return hash;
}
}
}