赞
踩
当我们下发DESCRIBE方法获取sdp数据的时候,神奇的一幕出现了。
DESCRIBE rtsp://192.168.1.65:554/cam/realmonitor?channel=1&subtype=0 RTSP/1.0
CSeq: 3
User-Agent: LibVLC/3.0.16 (LIVE555 Streaming Media v2016.11.28)
Accept: application/sdp
RTSP/1.0 401 Unauthorized
CSeq: 3
WWW-Authenticate: Digest realm="IP Camera(G8735)", nonce="251223aed58c9ed59d99e14dc6215ae1", stale="FALSE"
Date: Fri, Jun 24 2022 14:42:01 GMT
401 未认证,既然server大佬说了要认证,那作为client的小弟,就按部就班的进行认证,可以看出server大佬告诉了小弟,认证方法和一些参数,其中认证方式采用摘要认证(Digest),realm为“IP Camera(G8735)”,nonce为251223aed58c9ed59d99e14dc6215ae1。
在进行认证之前先了解一下摘要认证。如下图所示,当发送POST请求的时候,服务器返回401 Unauthorized,并且返回信息头中WWW-Authenticate:中包含了认证方式以及认证信息,其中:
realm:领域
nonce:这是由服务器规定的数据字符串,每次请求的时候这个参数都不一样,服务器根据自己的规则生成的。
qop:一般可以为“auth” “auth-int” 或者这个字段可以缺少,也就是服务器可以不返回。
algorithm:计算算法,如:MD5
算法 | A1 |
MD5 | A1 =MD5(<user>:<realm>:<password> |
MD5-sess | A1 = MD5(<usr>:<realm>:<password>):<nonce>:<cnonce> |
qop | A2 |
未定义 | MD5(<request-method>:<uri-directive-value>) |
auth | MD5(<request-method>:<uri-directive-value>) |
auth-int | MD5(<request-method>:<uri-directive-value>:H(<reuest-entity-body>)) |
qop | 摘要计算 |
未定义 | MD5(<A1>:<nonce>:<A2>) |
auth | MD5(<A1>:<nonce>:<nc>:<cnonce>:<qop>:<A2>) |
根据上述计算摘要步骤,我们一步一步进行计算,
首先根据Algorithm=“MD5”,如果服务器返回的该字段缺少,则依旧采用MD5,此时我们计算A1,A1=MD5(<user>:<realm>:<password>)
user:admin realm:example.com password:admin123456
A1=MD5(admin:example.com:admin123456)
A1=01584afdc23b82c514d4a6368c3b9530
由于server返回qop="auth",因此这里A2的计算方式根据上面公式可以得到
A2=MD5(<request-method>:<uri-directive-value>)
其中request-method表示请求的方法,这里请求的方法为POST
uri-directive-value表示请求uri跳转值,这里为/test_api/user/login
A2=MD5(POST:/test_api/user/login)
A2=0168a093f66f599ef4930de5b537a377
好了到此我们需要计算出摘要,也就是请求中的response带的参数
摘要计算是根据qop的值而不同,当qop为定义的时候,也就是server返回401的时候没有带qop参数,此时采用MD5(<A1>:<nonce>:<A2>) 而qop带参数的时候如"auth"或者"auth-int"的时候摘要计算方式为MD5(<A1>:<nonce>:<nc>:<cnonce>:<qop>:<A2>)
这里nc是一个计数器,累加,用一个十六进制8位的字符串表示,如demo中的00000001,cnonce是客户端生成的一个随机字符。
response=MD5(<A1>:<nonce>:<nc>:<cnonce>:<qop>:<A2>)
response=MD5(01584afdc23b82c514d4a6368c3b9530:ZXhhbXBsZS5jb206NTBmYTYyMzU6NA==:00000001:5cqIUuPK:auth:0168a093f66f599ef4930de5b537a377)
response=e9b7e9780568c90c6011e1980013542f
至此我们的摘要计算完成。
代码也是通过一步一步实现摘要计算,通过先计算A1,A2,最后再计算摘要
- static void md5_A1(char A1[33],const char *algorithm,const char *usr,const char *pwd,const char *realm,const char *nonce,const char *cnonce)
- {
- //A1 = <user>:<realm>:<password>
- //md5(A1)
- MD5_CTX ctx;
- unsigned char md5[16];
- MD5_Init(&ctx);
- MD5_Update(&ctx,(unsigned char*)usr,(unsigned int)strlen(usr));
- MD5_Update(&ctx,(unsigned char*)":",1);
- MD5_Update(&ctx,(unsigned char*)realm,(unsigned int)strlen(realm));
- MD5_Update(&ctx,(unsigned char*)":",1);
- MD5_Update(&ctx,(unsigned char*)pwd,(unsigned int)strlen(pwd));
- MD5_Final(md5,&ctx);
-
- base16(A1,md5,16);
- }
- static void md5_A2(char A2[33],const char *method,const char *uri,const char *qop)
- {
- //A2=<request-method>:<url-directive-value>
- //md5(A2)
- MD5_CTX ctx;
- unsigned char md5[16];
-
- MD5_Init(&ctx);
- MD5_Update(&ctx,(unsigned char*)method,(unsigned int)strlen(method));
- MD5_Update(&ctx,(unsigned char*)":",1);
- MD5_Update(&ctx,(unsigned char*)uri,(unsigned int)strlen(uri));
- if(0 == strcmp(qop,"auth-int")){
- //TODO
- }
- MD5_Final(md5,&ctx);
- base16(A2,md5,16);
- }
- static void md5_response(char *response,const char *A1,const char *A2,const char *nonce,int nc,const char *cnonce,const char *qop)
- {
- //MD5(MD5(A1):<nonce>:<nc>:<conce>:<qop>:MD5(A2))
- MD5_CTX ctx;
- unsigned char md5[16];
- char hex[32];
-
- memset(hex,0x00,sizeof(hex));
- MD5_Init(&ctx);
- MD5_Update(&ctx,(unsigned char*)A1,32);
- MD5_Update(&ctx,(unsigned char*)":",1);
- MD5_Update(&ctx,(unsigned char*)nonce,(unsigned int)strlen(nonce));
- MD5_Update(&ctx,(unsigned char*)":",1);
- if(*qop){
- snprintf(hex,sizeof(hex),"%08x",nc);
- MD5_Update(&ctx,(unsigned char*)hex,(unsigned int)strlen(hex));
- MD5_Update(&ctx,(unsigned char*)":",1);
- MD5_Update(&ctx,(unsigned char*)cnonce,(unsigned int)strlen(cnonce));
- MD5_Update(&ctx,(unsigned char*)":",1);
- MD5_Update(&ctx,(unsigned char*)qop,(unsigned int)strlen(qop));
- MD5_Update(&ctx,(unsigned char*)":",1);
- }
- MD5_Update(&ctx,(unsigned char*)A2,32);
- MD5_Final(md5,&ctx);
- base16(response,md5,16);
-
- }
- static void base16(char* str, const uint8_t* data, int bytes)
- {
- int i;
- const char hex[] = "0123456789abcdef";
-
- for (i = 0; i < bytes; i++)
- {
- str[i * 2] = hex[data[i] >> 4];
- str[i * 2 + 1] = hex[data[i] & 0xF];
- }
-
- str[bytes * 2] = 0;
- }
然后在封装RTSP协议中的认证字段,也就是Authorization响应头。
- int http_header_auth(struct http_www_authenticate_t *auth,char *pwd,char *method)
- {
- char A1[33];
- char A2[33];
- memset(A1,0x00,sizeof(A1));
- memset(A2,0x00,sizeof(A2));
- //根据信息计算出respone
- if(NULL == auth){
- return -1;
- }
- printf("auth->cnonce is %s\n",auth->cnonce);
- //nc+1
- auth->nc += 1;
- if(auth->scheme == HTTP_AUTH_DIGEST){
- md5_A1(A1,auth->algorithm,auth->username,pwd,auth->realm,auth->nonce,auth->cnonce);
- md5_A2(A2,method,auth->uri,auth->qop);
- md5_response(auth->response,A1,A2,auth->nonce,auth->nc,auth->cnonce,auth->qop);
- return 0;
- }
-
- return -1;
- }
- int digest_auth_authorization(struct http_www_authenticate_t *auth,char *pwd,char *method,char *auth_buf,int auth_buf_size)
- {
- if(NULL == auth || NULL == pwd || NULL == method || NULL == auth_buf)
- {
- return -1;
- }
- if(http_header_auth(auth,pwd,method) < 0){
- return -1;
- }
-
- snprintf(auth_buf,auth_buf_size,"Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\"",
- auth->username,auth->realm,auth->nonce,auth->uri,auth->response);
- return 0;
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。