OpenID - 生成签名

时间:2011-05-12 08:45:52

标签: openid

我一直在编写我自己的Open ID RP实现(是的,我知道已经有很多已经构建的,我正在为“有趣”而这样做)。一切正常,直到我进入验证步骤并计算哈希值并将其与我在正面断言中从OP得到的信号进行比较。

我上下阅读了这个规格,但有些事情对我来说并不清楚:

  1. 我是否只在openid命名空间中包含键值对,或者在openid.signed列表中包含所有内容? 6.1让我觉得我应该只使用openid。即使我在openid.signed(ax)中有一些其他东西挂起来。

  2. 最后一个键值对是否应该换行?

  3. 我假设值应该是url编码的(因为值中没有冒号)。如果是这样,我还假设十六进制值,例如%3D,应该是大写的。我在OAuth 1.0实现上遇到了这个问题,因为.NET的内置URL编码使用小写十六进制字母。

  4. 我很确定编码和算法都很好,但我的基本字符串已关闭。这是一个完全不变的例子,我无法开始工作:

    Querystring我从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 个答案:

答案 0 :(得分:2)

我在生成匹配签名时也遇到了问题,最后找到了解决方案。

  1. 正如您已经怀疑的那样,您必须添加openid.ax命名空间中的值,并应用添加不带openid.前缀的键/值对的相同规则。如果没有openid.ax个密钥,那么就会出现问题。

  2. 是的,最后一个键/值对后跟换行符(注意:只有\n)。这可以在OpenID规范中更清楚地提到。

  3. 您对网址编码错误,反之亦然:值必须为网址解码。这也没有在规范中明确说明。不要混淆冒号和分号,不允许使用冒号,但只允许使用关键部分,因此没有问题。

  4. 因此,如果您尝试使用此字符串并添加缺少的键/值对,则它应该起作用:

    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),它需要两个参数:

    • 成功进行OpenID身份验证(包含肯定断言密钥)后的完整重定向URL,可以从Web浏览器的地址栏中复制
    • Base64编码的MAC密钥,在先前的关联响应中返回

    代码:

    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));
        }
    
    }