2

我正在使用一个 Web 服务,该服务需要在soap 请求的标头中使用 wsse 安全性,并且由创建的、nonce 和密码摘要组成。Web 服务器使用这些值来授权真正的请求。

密码摘要是使用 Web 服务 API 中所述的以下算法创建的:

密码摘要 OASIS Usertoken 配置文件定义并描述了计算在 XML For Shipping API 中提交的唯一 Password_Digest 字符串的公式,此公式中使用的密码信息是纯文本密码的 SHA-1 哈希的 base 64 编码。

用于构造 Password_Digest 值的公式是

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

请注意,上述算法中的 + 符号表示三个字符串的字符串连接:来自 xml 请求的 Nonce、由 xml 请求创建和密码的 SHA-1 摘要的 Base64 编码。

我正在处理一个已知已成功授权的示例 XML 请求。我遇到的问题是,当我尝试使用示例中提供的值重新创建密码摘要时,我得到的密码摘要值不同。

示例值和预期的密码摘要:
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!";

方法一

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=

方法二

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=

方法三

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=

方法四

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==

这些方法都没有创建与样本匹配的密码摘要。方法 1 - 3 至少创建了与样本具有相同字符数的密码摘要,所以我是否正确假设编码在字符串的最终字节数匹配的意义上是部分正确的?

我的问题是任何人都可以帮助使用提供的示例值重新创建 password_digest 吗?

我只想提一下这是我第一次使用 ws-security 并且很少接触 SHA-1 和字符串编码/解码,因此非常感谢任何帮助。

笔记:

示例中的 nonce 字符串以 '==' 结尾,表明它已被某种方式编码,并且 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 的新值并得到以下结果

方法一、二

//passwordDigest = WcuTBY2W06vv2/JemRuorgxMCns=

方法三

//passwordDigest = KPHT7/ojTkvI6kJCaojbp0wKFZ4=

方法四

//passwordDigest = NzRBOTM2NUQ2RjAyMjEzN0E1NEVCN0Q0NEExODU2M0U4Q0FEMDkyQg==

所以再次没有成功,方法 3 的结果与方法 1 和 2 不同,这是出乎意料的,因为之前的随机数方法 1、2 和 3 给出了相同的结果。

4

1 回答 1

3

我使用示例和公式,然后我发现需要首先将 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;
        }
    }
}
于 2014-08-13T08:49:14.707 回答