Токен XMPP rspauth должен содержать что в аутентификации DIGEST-MD5?

Предыстория: я возился с сервером XMPP, который не работает.

Таким образом, документация по этому конкретному токену ответа под названием rspauth нигде не очень хорошо документирована. Некоторые, кажется, просто пропускают его и используют статическую строку, выглядящую так:

cnNwYXV0aD1lYTQwZjYwMzM1YzQyN2I1NTI3Yjg0ZGJhYmNkZmZmZA==

Что b64 декодирует в:

rspauth=ea40f60335c427b5527b84dbabcdfffd

Однако на последних этапах аутентификации MD5-DIGEST я якобы должен отправить следующее:

<challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
   cnNwYXV0aD1lYTQwZjYwMzM1YzQyN2I1NTI3Yjg0ZGJhYmNkZmZmZA==
</challenge>

Опять же, я не уверен, почему все и везде в каждом отчете об ошибке используют эту статическую строку. Но любой клиент XMPP ответит:

jabber: Error is -10 : SASL(-10): server failed mutual authentication step: DIGEST-MD5: This server wants us to believe that he knows shared secret

Я стараюсь максимально точно следовать RFC, и это то, что говорится в RFC о rspauth=:

Сервер получает и проверяет «дайджест-ответ». Сервер
проверяет, что счетчик одноразовых номеров равен "00000001". Если он поддерживает последующую
аутентификацию (см. раздел 2.2), он сохраняет значение одноразового номера и счетчика одноразовых номеров. Он отправляет сообщение в следующем формате:

response-auth = "rspauth" "=" response-value

где ответное значение вычисляется, как указано выше, с использованием значений, отправленных на втором шаге, за исключением того, что если qop равно "auth", то A2 равно

   A2 = { ":", digest-uri-value }

Исходя из этого, вот как я строю свой rspauth:

rspauth=b64enc(ByteConv('rspauth=:'+md5_this("xmpp/example.com")))

Что сводится к:

# md5_this == 6dae15e9021a0103e8e09ce86956a659 (obv not with example.com)
respauth = 'cnNwYXV0aD02ZGFlMTVlOTAyMWEwMTAzZThlMDljZTg2OTU2YTY1OQ=='
cli.respond('<challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>cnNwYXV0aD02ZGFlMTVlOTAyMWEwMTAzZThlMDljZTg2OTU2YTY1OQ==</challenge>')

Согласно этой теме по этому вопросу, последнее, что я отправляю неправильно, я должен отправить:

<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>cnNwYXV0aD02ZGFlMTVlOTAyMWEwMTAzZThlMDljZTg2OTU2YTY1OQ==</success>

Это также не удается, а затем клиент отправляет </stream:stream>, и соединение разрывается.
С <challenge>...


Вот тут-то я и запутался, предполагаю, что создаю токен rspauth=... неправильно, но я не знаю, что это должно быть.

Вот полная трассировка связи между Pidgin и сервером XMPP:

client connected
<< <?xml version='1.0' ?>
<< <stream:stream to='example.com' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>

>> Sending: <?xml version='1.0'?>
                <stream:stream xmlns:stream='http://etherx.jabber.org/streams' version='1.0' from='example.com' id='d86961dc-bfb5-4578-aa45-116d5f14ef54' xml:lang='en' xmlns='jabber:client'>
                    <stream:features>
                        <starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'><required/></starttls>
                        <register xmlns='http://jabber.org/features/iq-register'/></stream:features>

<< <starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>
>> Sending: <proceed xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>
 - Secure connection established [TLS]

<< <stream:stream to='example.com' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>
>> sending: <?xml version='1.0'?>
                <stream:stream xmlns:stream='http://etherx.jabber.org/streams' version='1.0' from='example.com' id='516f7395-4112-4892-87f1-2e9f7f3a96e1' xml:lang='en' xmlns='jabber:client'>
                    <stream:features>
                        <mechanisms xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
                            <mechanism>DIGEST-MD5</mechanism>
                        </mechanisms>
                    <auth xmlns='http://jabber.org/features/iq-auth'/>
                </stream:features>

<< <auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='DIGEST-MD5' xmlns:ga='http://www.google.com/talk/protocol/auth' ga:client-uses-full-bind-result='true'/>
>> Sending: <challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>cmVhbG09ImV4YW1wbGUuY29tIixub25jZT0iMTE2Iixxb3A9ImF1dGgiLGNoYXJzZXQ9dXRmLTgsYWxnb3JpdGhtPW1kNS1zZXNz</challenge>

<< <response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>dXNlcm5hbWU9InRvcnhlZCIscmVhbG09ImV4YW1wbGUuY29tIixub25jZT0iMTE2Iixjbm9uY2U9IjZGUDF5RUtBRk1TN2lHSnRBNlNiME5oQ1JBcmhGU0t3OHRMa2xJVEJPZGs9IixuYz0wMDAwMDAwMSxxb3A9YXV0aCxkaWdlc3QtdXJpPSJ4bXBwL2V4YW1wbGUuY29tIixyZXNwb25zZT1jODhmNTRiMjJlMmFiZGI4ZThlMTljOWVjZDliYjAxOCxjaGFyc2V0PXV0Zi04</response> 
>> Sending: <challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>cnNwYXV0aD1lYTQwZjYwMzM1YzQyN2I1NTI3Yjg0ZGJhYmNkZmZmZA==</challenge>
   DEBUG: rspauth=ea40f60335c427b5527b84dbabcdfffd

<< </stream:stream>

Я следовал этим руководствам RFC:

И взглянул на эти исходные коды:

И, по сути, осталось перепроектировать успешное соединение и еще больше разбить ответы, но хеши md5 сложно своевременно реверсировать, поэтому на этот раз я прошу помощи.

Нашел что-то

Проверил некоторые старые исходные коды и нашел следующее:

respauth = step_4 + ':' + nonce + ':' + cresp['nc'] + ':' + cnonce + ':' + cresp['qop'] + ':' + step_5
rspauth = 'rspauth=' + md5_this(rspauth)

Теперь он по-прежнему генерирует ту же ошибку, но это нечто иное, чем статические строки, поэтому я пока с этим работаю.


person Torxed    schedule 16.01.2016    source источник


Ответы (1)


Я знаю, что этому 4 месяца, но у меня была точно такая же проблема, и я, кажется, решил ее.

rfc для дайджеста sasl (https://tools.ietf.org/html/rfc2831#section-2.1.3)

Я сделал код на C#, но его должно быть легко освоить, я использовал имена функций, как они используются в RFC.

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

!ВНИМАНИЕ! MD5-Digest считается небезопасным, и новым стандартом является SCRAM SASL (https://tools.ietf.org/html/rfc5802)

Метод проверки хэша клиента

public string GenerateClientHash(string user, string realm, string password, string nonce, string cnonce, string authzid, string digesturi, string qop, string nc)
            {
                byte[] A2 = (qop.Equals("auth") ? Encoder.GetBytes($"AUTHENTICATE:{digesturi}"): Encoder.GetBytes($"AUTHENTICATE:{digesturi}:00000000000000000000000000000000"));

                byte[] h1 = H(Encoder.GetBytes($"{user}:{realm}:{password}"));
                byte[] h2 = Encoder.GetBytes($":{nonce}:{cnonce}");

                byte[] A1 = Merge(h1,h2);
                byte[] response_value = HEX(KD(HEX(H(A1)),
                    Merge(Encoder.GetBytes($"{nonce}:{nc}:{cnonce}:{qop}:"), HEX(H(A2)))
                    ));

                byte[] h3 = Merge(h1, h2);
                return Encoder.GetString(response_value);
            }

Сгенерировать хэш сервера (тот, с которым у вас были проблемы)

public string GenerateServerHash(string user, string realm, string password, string nonce, string cnonce, string authzid, string digesturi, string qop, string nc)
        {
            byte[] A2 = (qop.Equals("auth") ? Encoder.GetBytes($":{digesturi}") : Encoder.GetBytes($":{digesturi}:00000000000000000000000000000000"));

            byte[] h1 = H(Encoder.GetBytes($"{user}:{realm}:{password}"));
            byte[] h2 = Encoder.GetBytes($":{nonce}:{cnonce}");

            byte[] A1 = Merge(h1, h2);
            byte[] response_value = HEX(KD(HEX(H(A1)),
                Merge(Encoder.GetBytes($"{nonce}:{nc}:{cnonce}:{qop}:"), HEX(H(A2)))
                ));

            byte[] h3 = Merge(h1, h2);
            return Encoder.GetString(response_value);
        }

Функции, упомянутые в RFC.

/// <summary>
        /// Let H(s) be the 16 octet MD5 hash [RFC 1321] of the octet string s.
        /// </summary>
        /// <param name="s">Byte Array Af streng for at sikre encodning</param>
        /// <returns>s -> Base16(s) = s16 -> MD5Hash(s16)</returns>
        public byte[] H(byte[] s)
        {
            MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
            return md5.ComputeHash(s);
        }
        /// <summary>
        /// Let KD(k, s) be H({k, ":", s}), i.e., the 16 octet hash of the string k, a colon and the string s.
        /// </summary>
        /// <param name="k">Byte Array Af streng for at sikre encodning</param>
        /// <param name="s">Byte Array Af streng for at sikre encodning</param>
        /// <returns>k + ":" + s = ks -> H(ks)</returns>
        public byte[] KD(byte[] k, byte[] s)
        {
            byte[] colon = Encoder.GetBytes(":");
            byte[] ks = Merge(k, colon, s);
            return H(ks);
        }
        /// <summary>
        /// Let HEX(n) be the representation of the 16 octet MD5 hash n as a string of 32 hex digits(with alphabetic characters always in lower case, since MD5 is case sensitive).
        /// </summary>
        /// <param name="n">Byte Array Af streng for at sikre encodning</param>
        /// <returns>lowercase of n</returns>
        public byte[] HEX(byte[] n)
        {
            byte[] b = Encoder.GetBytes(toBase16(n).ToLower());
            return b;
        }
person Droa    schedule 03.06.2016
comment
Мне нужно было как-то это проверить и продолжить с того места, где я остановился. Но я благодарен за ваш ответ, независимо от того, сколько времени прошло с тех пор, как я задал вопрос :) - person Torxed; 03.06.2016