OpenID - Генерация подписи - PullRequest
3 голосов
/ 12 мая 2011

Я писал свою собственную реализацию Open ID RP (да, я знаю, что их уже много, я делаю это для «удовольствия»).Все работает нормально, пока я не на шаге проверки, не вычисляю хеш и не сравниваю его с сигнатурой, которую я получил от OP в положительном утверждении.

Я прочитал спецификацию вверх и вниз, но есть нескольковещи, которые мне не были понятны:

  1. Включаю ли я только пары ключ-значение в пространство имен openid или все, что находится в списке в openid.signed? 6.1 звучит так, как будто я должен использовать только openid.ключи, хотя у меня есть другие вещи, висящие в openid.signed (ax).

  2. Должна ли последняя пара значений ключа следовать за разрывом строки?

  3. Я предполагаю, что значения должны быть закодированы в URL (чтобы в значении не было двоеточий).Если это так, я бы также предположил, что шестнадцатеричные значения, такие как% 3D, должны быть в верхнем регистре.Я столкнулся с этим в реализации OAuth 1.0, поскольку встроенная кодировка URL в .NET использует шестнадцатеричные буквы в нижнем регистре.

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

Строка запроса Я возвращаюсь из OP с положительным утверждением: openid.ns=http://specs.openid.net/auth/2.0&openid.mode=id_res&openid.op_endpoint=https://www.google.com/accounts/o8/ud&openid.response_nonce=2011-05-13T08:18:42ZBHyiLFGyNT-SqQ&openid.return_to=http://mysite.com/Account/Login.aspx&openid.assoc_handle=AOQobUc4P9MWC3faGcMkfTb2U10KfGQ-6cm9L4pLDQmeoY2DE6XRGtN0&openid.signed=op_endpoint,claimed_id,identity,return_to,response_nonce,assoc_handle,ns.ext1,ext1.mode,ext1.type.firstname,ext1.value.firstname,ext1.type.email,ext1.value.email,ext1.type.lastname,ext1.value.lastname&openid.sig=KSXw+bv7sLlQyUIflA3Jzx5VoPk=&openid.identity=https://www.google.com/accounts/o8/id?id=AItOawkDYxJln6LwTAdl0kP8xdMT71SoRufUFA4&openid.claimed_id=https://www.google.com/accounts/o8/id?id=AItOawkDYxJln6LwTAdl0kP8xdMT71SoRufUFA4&openid.ns.ext1=http://openid.net/srv/ax/1.0&openid.ext1.mode=fetch_response&openid.ext1.type.firstname=http://axschema.org/namePerson/first&openid.ext1.value.firstname=firstname&openid.ext1.type.email=http://schema.openid.net/contact/email&openid.ext1.value.email=testingopenid5132011@gmail.com&openid.ext1.type.lastname=http://axschema.org/namePerson/last&openid.ext1.value.lastname=lastname

Базовая строка, которую я построилиспользуя эту строку запроса: op_endpoint:https://www.google.com/accounts/o8/ud\nclaimed_id:https://www.google.com/accounts/o8/id?id=AItOawkDYxJln6LwTAdl0kP8xdMT71SoRufUFA4\nidentity:https://www.google.com/accounts/o8/id?id=AItOawkDYxJln6LwTAdl0kP8xdMT71SoRufUFA4\nreturn_to:http://mysite.com/Account/Login.aspx\nresponse_nonce:2011-05-13T08:18:42ZBHyiLFGyNT-SqQ\nassoc_handle:AOQobUc4P9MWC3faGcMkfTb2U10KfGQ-6cm9L4pLDQmeoY2DE6XRGtN0\nns.ext1:http://openid.net/srv/ax/1.0\next1.mode:fetch_response\next1.type.firstname:http://axschema.org/namePerson/first\next1.value.firstname:firstname\next1.type.email:http://schema.openid.net/contact/email\next1.value.email:testingopenid5132011@gmail.com\next1.type.lastname:http://axschema.org/namePerson/last\next1.value.lastname:lastname\n

Ключ Mac, возвращаемый запросом на ассоциацию: U/1wUBAU2aYIR+2eIsugXyEOpmE=

Используя все это с HMAC-SHA1, я получаю хеш-код: 9HMRL4je44Oz90s1f8pw5qpZ8HQ=

Но, как вы можете видеть из openid.sig, это должно быть KSXw+bv7sLlQyUIflA3Jzx5VoPk=

Я неправильно сформулировал базовую строку?Я неправильно вычисляю хеш?Как что-то такое «простое» занимает так много времени для правильной реализации?

1 Ответ

2 голосов
/ 13 мая 2011

У меня также были проблемы с генерацией подходящей подписи, и, наконец, я нашел решение.

  1. Как вы уже подозревали, вы должны добавить значения из пространства имен openid.ax, применяя то же правило добавления пары ключ / значение без префикса openid.. Если нет ключей openid.ax, значит что-то не так.

  2. Да, за последней парой ключ / значение следует новая строка (внимание: только \n). Это могло бы быть более четко упомянуто в спецификации OpenID.

  3. Вы ошибаетесь в кодировке URL, все наоборот: значения должны быть декодированными URL . Также это явно не указано в спецификации. Не путайте двоеточия и точки с запятой, вам не разрешено иметь двоеточия , но только в ключевой части, поэтому проблем с этим нет.

Так что, если вы попробуете с этой строкой и добавите отсутствующие пары ключ / значение, она должна работать:

ns:http://specs.openid.net/auth/2.0
op_endpoint:https://www.google.com/accounts/o8/ud
claimed_id:https://www.google.com/accounts/o8/id?id=AItOawlvj7acGYj-NH1kKKl3RswJlLCKpl9LIwk
identity:https://www.google.com/accounts/o8/id?id=AItOawlvj7acGYj-NH1kKKl3RswJlLCKpl9LIwk
return_to:http://mysite.com/Account/Login.aspx
response_nonce:2011-05-12T03:56:09ZoeDC9WFOgOBaAQ
assoc_handle:AOQobUdHugprvbsK2-8NCtS2uBomRDGJQGOKDmqEwxco8Rny47rdZlBp
ns.ext1:http://openid.net/srv/ax/1.0
ext1.mode:fetch_response
ext1.type.firstname:http://axschema.org/namePerson/first
ext1.value.firstname:First
ext1.type.email:http://schema.openid.net/contact/email
ext1.value.email:myemail@gmail.com
ext1.type.lastname:http://axschema.org/namePerson/last
ext1.value.lastname:Name

Это маленькое консольное приложение повторно генерирует подпись (используя HMAC-SHA256), для этого нужны два параметра:

  • полный URL-адрес перенаправления после успешной аутентификации OpenID (содержащий положительные ключи подтверждения), можно скопировать из адресной строки веб-браузера
  • ключ MAC в кодировке Base64, как возвращено в предыдущем ответе на ассоциацию

Код:

using System;

public class OpenIdSignatureVerification {

    public static void Main(string[] args) {
        if (args.Length != 2) {
            Console.Error.WriteLine("Usage: assertion_url mac_key");
            Environment.Exit(1);
        }

        string url = args[0];
        int pos = url.IndexOf('?');
        if (pos == -1) {
            Console.Error.WriteLine("No query string found");
            Environment.Exit(1);
        }
        url = url.Substring(pos + 1);
        Console.WriteLine(String.Format("Query string: {0}", url));

        System.Collections.Generic.Dictionary<string, string> dict = new System.Collections.Generic.Dictionary<string, string>();

        foreach (string part in url.Split('&')) {
            string[] keyValue = part.Split('=');
            if (keyValue.Length != 2) continue;
            dict[keyValue[0]] = System.Web.HttpUtility.UrlDecode(keyValue[1]);
        }

        string hashInput = String.Empty;
        string[] signed = dict["openid.signed"].Replace("%2C", ",").Split(',');
        foreach (string key in signed) hashInput += key + ":" + dict["openid." + key] + "\n";

        string macKey = args[1];

        Console.WriteLine(String.Format("Hash input: {0}\n", hashInput));
        Console.WriteLine(String.Format("MAC Key: {0}", macKey));

        byte[] encodedHashInput = System.Text.Encoding.UTF8.GetBytes(hashInput);

        System.Security.Cryptography.HMACSHA256 signer = new System.Security.Cryptography.HMACSHA256(Convert.FromBase64String(macKey)); 

        string hashOutput = Convert.ToBase64String(signer.ComputeHash(encodedHashInput));

        Console.WriteLine(String.Format("Signature hash (expected)  : {0}", dict["openid.sig"]));
        Console.WriteLine(String.Format("Signature hash (calculated): {0}", hashOutput));
    }

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...