【rtsp client取海康IPC H264视频流】——摘要认证

        当我们下发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,最后再计算摘要

A1的计算方式:

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

A2的计算方式:

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

response计算(摘要):

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

猜你喜欢

转载自blog.csdn.net/weihan0208/article/details/125850977