现在的位置: 首页 > 综合 > 正文

http协议之digest(摘要)认证

2012年01月16日 ⁄ 综合 ⁄ 共 3078字 ⁄ 字号 评论关闭

参考网址:

http://www.faqs.org/rfcs/rfc2617.html

http://www.faqs.org/rfcs/rfc1321.html

http://www.cnblogs.com/my_life/articles/2285649.html

http://blog.sina.com.cn/s/blog_53b15ed5010006t9.html

http://blog.chinaunix.net/uid-26212859-id-3485297.html

http://blog.csdn.net/jackxinxu2100/article/details/5610448#reply

http://www.vsharing.com/k/KM/2003-1/458471.html

http://support.microsoft.com/kb/811318/zh-cn

其中前面的5个是重要的,而前面2个是最重要的

 

网上下载了一个thttpd的web服务器做为嵌入式的web服务,在完成了所有的功能之后,提出基于web服务httpd协议的所有功能都要进行保护,即进行认证。然后从网上开始找相关的资料,经过几天的时间,终于弄好了。

在此不讨论basic认证,因为basic认证过于简单,直接传送的明文。

我不理解这个digest单词的含义,也不理解rfc2617文档中digest-challenge的含义,现在还没有转过来呢。

首先说明,以下的过程,只是基于我的内容方面的描述,可能不够全面,所以本文只做为普通参考。

认证的过程总体上是:客户端请求资源->服务器返回认证标示->客户端发送认证信息->服务器查验认证,如果成功则继续资源传送,否则直接断开连接。

服务器返回认证标示的必要内容如下:

WWW-Authenticate: Digest realm="xx",qop="auth,auth-int",nonce="xx",opaque="xx"

这个部分需要和http的协议头部分一起,即放在第一个空行的前面,其中的双引号也一同传输。在rfc2616文档中还定义了很多的其它域,各域之间都用“,”号隔开,这里用不到,不进行讨论。各域的值中,不允许出现冒号“:”,因为冒号在md5加密时是个连接符。另外所有的值都必需是可见字符,这和http协议的要求相一致。这个部分的内容很严格,如果弄不好,则客户端可能会提示错误,比如“协议冲突”、“拒绝服务”等情形

同时返回401的Unauthorized错误。

默认是md5加密,而且md5应该是用的最广的,所以对于algorithm域,无需添加。

WWW-Authenticate是httpd的一个标头

realm的值是一个简单的字符串,而rfc2617上写的是一个email类型的字符串,我看这个没有必要意义,所以我就也简单地写了个email形式的字符串

qop是认证的(校验)方式,这个比较重要,对后面md5的加密过程有影响,值就按照上面的那样写就行了

nonce的值也是一个字符串,如果不严格,可以随机生成一个就行,注意它是个GUID,即唯一的、不重复的。如果严格,则需要包含时间信息、客户端IP信息和其它信息,因为认证过程的时间很短,所以如果服务器收到认证信息后发现这个时间和服务器的时间相去甚远,那说明不正常,直接拒绝,以防止攻击,还有客户端IP,如果这个IP一直这样攻击,则可以在一定时间内发现是该IP的连接则直接断掉。这些严格的做法主要是为了防止攻击。在rfc2617上有狭路为详细的描述。我这里没有考虑这些,只是使用了个简单的字符串

opaque是个字符串,它只是透传而已,即客户端还会原样返回过来。实际上,上面的那些域,客户端都还是会原样返回的,但返回时除了以上的那些域之外,还会增加新的内容进来。

客户端在收到401错误之后,根据上面的那些信息,需要用户输入用户名和密码,然后返回给服务器如下内容:

Authorization: Digest username="xx",realm="xx",qop="auth",nonce="xx",uri="path",cnonce="xx",nc=00000001,response="xx",opaque="xx"

除了qop的值不一样之外,其它存在的那些域的值都是一样的,新增加的部分:

username是要认证的用户名

uri是本次请求的资源位置,比如“/public/userinfo.htm”

cnonce是客户端产生的一个GUID,一般是32字节,而且是16个字节字符串的16进制形式表示,所以其中的内容是0~9和a~f之间的那些字符。其实nonce也是一样,但我没有特意这样做,比如我直接生成一个数字,然后以16进制输出,而没有特殊追求16个字节。

nc是认证的次数,因为如果认证失败,则仍然可以重新发送认证信息继续认证,第一次是1,第二次是2,第三次是3,...。但我这里没有这样弄,只允许一次,如果认证不过就关闭了连接,所以我只处理了1的情况,即00000001,这个值总是固定的8个字节,而且不加引号,和其它域的格式不一样,导致我在分析这个域的时候单独写了个代码,晕,不知道为什么会定义的不一样。

response的值就很重要了,是根据以上信息,再加上密码通过一定的顺序计算出的一个md5码,固定为16字节的16进制表示形式。服务器在收到所有这些信息后,也通过相同的方式计算出这个值,而密码则是保存在服务器端,即服务器要通过用户名去找到对应的密码,然后和计算出md5值,再和客户端传过来的response值对比,如果一样,则认证通过,否则通不过。

 

关键的是md5值的计算,用各域的值进行计算(不带两端的双引号)。

总的计算公式是:md5=HA1:HD:HA2

注意,这个公式被我简化了,只是为了便于描述而和其它文档上的形式不一样。表示的是对HA1:HD:HA2进行md5计算,下同。

md5表示最终计算出来的值,即response的值

注意之间的冒号“:”也是计算的字符串中的内容,是个连接符

如果algorithm的值是“md5-sess”,则

HA1=username:realm:password:nonce:cnonce              (1)

否则

HA1=username:realm:password                                     (2)

我这里用的是(2)

如果qop有值(if (*pszQop)),则

HD=nonce:noncecount:cnonce:qop                                (3)

否则

HD=nonce                                                                       (4)

由于我这里qop有值,所以用了(3)

HA2=method:uri

如果qop的值是“auth-int”,则HA2的值计算不一样,我看的不是很懂,而且我没有用,所以这个我没有管它。

method是指“GET”/"POST",即http头中指定的获取资源的方式

 

需要注意的是,在计算过程中,需要分配足够的内在,否则越界可能导致字符串内容的变化,我的就因为这个问题搞了1天的时间才找到,最后从网上找了个md5加密的计算工具,把各部分的计算结果进行对照才发现了问题。

实际上还找了另外的一个峙址,公式写的比较好,比较清楚,但后来没有收藏。

抱歉!评论已关闭.