C# Кодировка wsse security password_digest с SHA-1 и Base64 не совпадает с сервером password_digest

Я использую веб-сервис, который требует безопасности wsse в заголовке запросов мыла и состоит из созданного, одноразового номера и пароля. Веб-сервер использует эти значения для авторизации подлинного запроса.

Дайджест пароля создается с использованием следующего алгоритма, как указано в API веб-службы:

Дайджест пароля Профиль OASIS Usertoken определяет и описывает формулу, которая вычисляет уникальную строку Password_Digest, представленную в API XML для доставки. Информация о пароле, используемая в этой формуле, является кодировкой base 64 хэша SHA-1 обычного текстового пароля.

Формула, используемая для построения значения Password_Digest:

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

Обратите внимание, что символ + в вышеприведенном алгоритме представляет собой конкатенацию трех строк: Nonce из xml-запроса, Created from xml request и кодирование Base64 дайджеста SHA-1 пароля.

Я работаю с примером запроса XML, который, как известно, был успешно авторизован. Проблема, с которой я сталкиваюсь, заключается в том, что я получаю другое значение для дайджеста пароля, когда пытаюсь воссоздать его со значениями, приведенными в примере.

Примеры значений и ожидаемый password_digest:
Одноразовый номер: 4ETItj7Xc6+9sEDT5p2UjA==
Создано: 2014-08-04T10:22:48.994Z
Пароль: Пароль2014!
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 в образце оканчивается на "==", предполагая, что она была закодирована как и как XML-схема для элемента Nonce выглядит следующим образом:

<wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">4ETItj7Xc6+9sEDT5p2UjA==</wsse:Nonce>

Что говорит о том, что одноразовый номер закодирован в base64, поэтому я попытался перезапустить методы снова, но одноразовый номер был преобразован в обычный текст со следующей строкой

nonce = Encoding.UTF8.GetString(Convert.FromBase64String(nonce));
//nonce = �Dȶ>�s���@�杔�

Исходя из этого, я не думаю, что компания API создаст одноразовый номер, как этот, или мои предположения неверны в отношении шифрования одноразового номера, или я неправильно его декодирую.

Я все еще пытался с новым значением для nonce и получил следующие результаты

Метод 1, 2

//passwordDigest = WcuTBY2W06vv2/JemRuorgxMCns=

Способ 3

//passwordDigest = KPHT7/ojTkvI6kJCaojbp0wKFZ4=

Способ 4

//passwordDigest = NzRBOTM2NUQ2RjAyMjEzN0E1NEVCN0Q0NEExODU2M0U4Q0FEMDkyQg==

Итак, опять нет успеха, и результат метода 3 отличается от метода 1 и 2, который был неожиданным, так как предыдущие методы одноразового использования 1, 2 и 3 дали те же результаты.

1 ответ

Решение

Я играю с примером и формулой, а затем я обнаружил, что существительное нужно сначала расшифровать в байты, а затем применить формулу

это полный пример с правильным результатом:

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;
        }
    }
}
Другие вопросы по тегам