我一直在编写我自己的Open ID RP实现(是的,我知道已经有很多已经构建的,我正在为“有趣”而这样做)。一切正常,直到我进入验证步骤并计算哈希值并将其与我在正面断言中从OP得到的信号进行比较。
我上下阅读了这个规格,但有些事情对我来说并不清楚:
我是否只在openid命名空间中包含键值对,或者在openid.signed列表中包含所有内容? 6.1让我觉得我应该只使用openid。即使我在openid.signed(ax)中有一些其他东西挂起来。
最后一个键值对是否应该换行?
我假设值应该是url编码的(因为值中没有冒号)。如果是这样,我还假设十六进制值,例如%3D,应该是大写的。我在OAuth 1.0实现上遇到了这个问题,因为.NET的内置URL编码使用小写十六进制字母。
我很确定编码和算法都很好,但我的基本字符串已关闭。这是一个完全不变的例子,我无法开始工作:
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=
我是否错误地制定了基本字符串?我计算哈希错了吗?这个“简单”的东西需要这么长时间才能正确实现?
答案 0 :(得分:2)
我在生成匹配签名时也遇到了问题,最后找到了解决方案。
正如您已经怀疑的那样,您必须添加openid.ax
命名空间中的值,并应用添加不带openid.
前缀的键/值对的相同规则。如果没有openid.ax
个密钥,那么就会出现问题。
是的,最后一个键/值对后跟换行符(注意:只有\n
)。这可以在OpenID规范中更清楚地提到。
您对网址编码错误,反之亦然:值必须为网址解码。这也没有在规范中明确说明。不要混淆冒号和分号,不允许使用冒号,但只允许使用关键部分,因此没有问题。
因此,如果您尝试使用此字符串并添加缺少的键/值对,则它应该起作用:
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),它需要两个参数:
代码:
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));
}
}