C#使用SHA-1和Base64不匹配服务器password_digest编码wsse security password_digest

时间:2014-08-12 11:16:54

标签: c# encoding base64 sha1 wsse

我正在使用一个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给出了相同的结果。

1 个答案:

答案 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;
        }
    }
}