其实这东西说白了就是你拿着“我是谁谁谁”的一张单子找OP签了个名,然后拿给RP验证。RP验证通过了就放你进去,否则就不放。

至于RP怎么验证OP的签名,有两种方法。一是RP事前和OP用Diffie-Hellman商量个密钥,这样RP和OP用同样的密钥签名,RP只需要检查自己签出来 的是不是和OP一样,这是HMAC的典型应用。二是RP把你给它的单子拿去问OP:“某某某说这是你给它签的,有效吗?”然后OP会告诉RP是否有效。OP只需要再签 一次然后比对是否相同就行了。在这种方法中RP并不知道OP签名所用的密钥。

为了防止重放攻击,有两个地方需要注意。第一,OP在签名的时候要签进去一个nonce,形式为“Unix时间戳+unique字符串”。必须保证同一个OP的任何两 次签名的nonce都不相同,而RP对同一个nonce仅允许验证成功一次。怎么理解呢?所谓重放攻击,就是你拿着OP给你签好的单子去RP验证通过的时候,这个单子 被某个坏人看到了(例如网路上的窃听者)。于是它记下了单子的数据,并伪造一张一样的单子去RP那儿验证。但是单子上有nonce这个东西,而且RP也会维护之前验证 成功的nonce。因为OP保证任何一个nonce只用一次,当RP看到一个nonce被用了两次的时候,就知道是坏人在重放了,所以会拒绝第二次请求,于是坏人就不 能得逞了。

那么RP如何维护这么庞大的已经使用过的nonce列表呢?这时时间戳就派上用场了。其实RP并不需要维护所有已经使用过的nonce列表,而是只需要维护时间戳误差 在RP与OP时钟漂移+网络延时范围内的就可以了。假设RP的时间是t,时钟漂移+网络延时是d,那么OP的时间范围是[t-d, t+d],所以RP就只需要维护时间戳在[t-d, t+d]的消息。时间戳大于t+d的签名消息根本不会出现,而时间戳小于t- d的被RP认为是重放。这样RP的负担就小多了。

回到主线接着说第二,在上述验证方法二中,OP对同一个验证请求最多只能回复一次。这其实和前面的道理是一样的,只不过角色从RP转移到了OP。

最后,什么叫做同一个验证请求?就是指nonce相同的呗。前面都说了一个nonce只能用一次,所以nonce是一个天然的唯一标识符。

不过后来我想了一下,是不是能把这两个地方合起来,把防止重放攻击的责任完全放在RP身上?因为无论采用哪种认证方式,都是RP最后得到验证成功或失败的消息。作为R P所需要保证的就是,无论采取哪种验证方式,都不能让同一个nonce成功两次就可以了。这么来看完全没有必要让OP管这些事。而协议里之所以要OP来插一腿,估计是 为了应付那些stateless的RP的,那些RP没脑子,记不住哪些nonce是曾经验证成功的,所以只能完全靠OP了。不知道我的分析对不对。

推荐三篇文章。第一篇讲什么是OpenID,第二篇讲OpenID工作原理,第三篇是OpenID Authentication 2.0协议原文。

Links: