TR069 Http Digest 认证流程

 

流程及流程图

1.1盒端主动发起Http Digest认证流程 

盒端CPE                                          ACS终端管理系统

1.------------------inform(http不带auth头)----------->

2.<------------------401(http不带auth头)--------------

3.------------------inform(http带auth头)------------->

4.<------------------200 OK---------------------------

5.------------------ Content-Length: 0--------------->

机顶盒(CPE)通过HTTP Digest Authentication发起与终端管理系统(ACS)的认证连接,连接方式遵循RFC 2617的规定。

机顶盒连接终端管理系统的地址由Device.ManagementServer.URL参数提供。

机顶盒主动想终端管理系统发起一个HTTP 连接请求,终端管理系统会要求进行HTTP Digest Authentication认证。并按照RFC2671规范,盒端和盒端管理系统在之后的请求和应答时信息均带有认证头信息。

认证中的用户名、密码为Device.ManagementServer.Username 及Device.ManagementServer.Password

1.2   ACS主动发起Http Digest认证流程

盒端CPE                                        ACS终端管理系统

1.<------------------http(不带auth头信息)-------------

2.------------------ 401 Unauthorized---------------->

3.<------------------http get(带auth头信息)-----------

4.------------------200 OK--------------------------->

5.<-----------------100 continue----------------------

6.------------------6 connect request---------------->

7.<------------------200 OK---------------------------

ACS终端管理系统主动发起一个HTTP 请求,CPE终端会要求进行HTTP Digest Authentication认证。并按照RFC2671规范,盒端和盒端管理系统在之后的请求和应答时信息均带有认证头信息。

认证中的用户名、密码为Device.ManagementServer.Connection.RequestUsername及Device.ManagementServer.ConnectionRequestPassword

 

二 详细交互流程:

2.1  盒端主动发起Http Digest认证报文 

 

CPE IP地址: 192.168.20.11

ACS IP地址: 192.168.20.36

1)盒端(CPE)首先发起不带认证头的Inform请求报文,内容如下:

POST /acs HTTP/1.1

Host: 192.168.20.36

Accept: */*

Connection: TE, Keep-Alive

Content-Type: text/xml; charset=utf-8

SOAPAction: ""

Content-Length: 3814

Expect: 100-continue

HTTP/1.1 100 Continue

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:cwmp="urn:dslforum-org:cwmp-1-0">

<SOAP-ENV:Header>

<cwmp:ID SOAP-ENV:mustUnderstand="1">1</cwmp:ID>

</SOAP-ENV:Header>

<SOAP-ENV:Body>

<cwmp:Inform>

<DeviceId xsi:type="cwmp:DeviceIdStruct">

<Manufacturer>Test</Manufacturer>

<OUI>A1B2C4</OUI>

<ProductClass>Test_PC</ProductClass>

<SerialNumber>821281000054321</SerialNumber>

</DeviceId>

<Event SOAP-ENC:arrayType="cwmp:EventStruct[2]">

<EventStruct>

<EventCode>1 BOOT</EventCode>

<CommandKey></CommandKey>

</EventStruct>

<EventStruct>

……..

2) 盒端管理系统(ACS)收到上述报文后,发现没有认证消息(带有Authorization:标识的报文),然后发送401错误报文:

HTTP/1.1 401 Unauthorized

Date: Fri, 06 Jan 2017 02:47:12 GMT

Expires: Thu, 01 Jan 1970 00:00:00 GMT

Set-Cookie: JSESSIONID=12rxzt10p2rtb;Path=/

Content-Type: text/xml; charset=utf-8

WWW-Authenticate: Digest realm="XACS",qop="auth",nonce="fd171d5efcc65e79bfd8150af7f9cb21"

Content-Length: 0

Server: Jetty(6.1.20)

3 ) 盒端(cpe)收到报文后,经过分析得到报文错误为401,代码中通过分析报文中是否有WWW-Authenticate:  Digest 字段,如果具有那么通过设置函数

//设置鉴权参数

code=curl_easy_setopt(curl,CURLOPT_HTTPAUTH,CURLAUTH_BASIC|CURLAUTH_DIGEST);

(本地配置的realm必须与收到的realm一致否则验证不能通过)将本地文件配置的realm和从盒端管理系统(ACS)收到的nonce,opaque,qop等值通过函数http_da_calc_HA1,生成一个唯一的字符串并存入response字段,并将这些信息组合到报文的头部,最后发送给ACS的报文为:

POST /acs HTTP/1.1

Authorization: Digest username="cpe", realm="XACS", nonce="fd171d5efcc65e79bfd8150af7f9cb21", uri="/acs", cnonce="MDQ1NzA0", nc=00000001, qop="auth", response="5f6059675ea5da97e45be615c2466ff7"

Host: 192.168.20.36

Accept: */*

Cookie: JSESSIONID=12rxzt10p2rtb

Connection: TE, Keep-Alive

Content-Type: text/xml; charset=utf-8

SOAPAction: ""

Content-Length: 3814

Expect: 100-continue

HTTP/1.1 100 Continue

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:cwmp="urn:dslforum-org:cwmp-1-0">

<SOAP-ENV:Header>

<cwmp:ID SOAP-ENV:mustUnderstand="1">1</cwmp:ID>

</SOAP-ENV:Header>

<SOAP-ENV:Body>

<cwmp:Inform>

<DeviceId xsi:type="cwmp:DeviceIdStruct">

<Manufacturer>Test</Manufacturer>

<OUI>A1B2C4</OUI>

<ProductClass>Test_PC</ProductClass>

<SerialNumber>821281000054321</SerialNumber>

</DeviceId>

<Event SOAP-ENC:arrayType="cwmp:EventStruct[2]">

<EventStruct>

<EventCode>1 BOOT</EventCode>

<CommandKey></CommandKey>

</EventStruct>……….

…….

4)盒端管理系统(ACS)收到上述报文后,确认其含Authorization:字段,并且Authorization:字段中的response的值正确,那么认证通过,并发送回复报文:

HTTP/1.1 200 OK

Date: Fri, 06 Jan 2017 02:47:12 GMT

Content-Type: text/xml; charset=utf-8

Content-Length: 526

Server: Jetty(6.1.20)

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:cwmp="urn:dslforum-org:cwmp-1-0" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><SOAP-ENV:Header><cwmp:ID SOAP-ENV:mustUnderstand="1">1</cwmp:ID><cwmp:NoMoreRequests>0</cwmp:NoMoreRequests></SOAP-ENV:Header><SOAP-ENV:Body><cwmp:InformResponse><MaxEnvelopes>1</MaxEnvelopes></cwmp:InformResponse></SOAP-ENV:Body></SOAP-ENV:Envelope>

5)  盒端(CPE)收到回复报文后,分析为认证通过报文后,发送一个inform内容为空的确认报文:——其实就是个空报文

POST /acs HTTP/1.1

Authorization: Digest username="cpe", realm="XACS", nonce="fd171d5efcc65e79bfd8150af7f9cb21", uri="/acs", cnonce="MDQ1NzA0", nc=00000002, qop="auth", response="1b58ed5321c916998e4af9de375177fc"

Host: 192.168.20.36

Accept: */*

Cookie: JSESSIONID=12rxzt10p2rtb

Connection: TE, Keep-Alive

Content-Length: 0

Content-Type: application/x-www-form-urlencoded

2.2   ACS主动发起Http Digest认证报文

略,和2.1流程类似,具体可以下载报文,自行分析。

2.3   同时开启双向认证 

即CPE终端认证ACS管理系统,和ACS管理系统认证CPE终端同时认证。成功后盒端和盒端管理系统在之后的请求和应答时信息均带有认证头信息,认证中的用户名、密码为Device.ManagementServer.Username 及Device.ManagementServer.Password

报文下载路径:

http://download.csdn.net/detail/eryunyong/9730525

代码片段:

1) CPE 认证ACS管理系统的代码

//初始化curl库,设置参数用于http 传输

 int http_init_curl(cwmp_context_t *cwmp_ctx, t_MemStruct *pmem, CURL **pcurl)

 {

     CURLcode    code;

     CURL          *curl = NULL;

     char           *acs_usr = NULL;

     char           *acs_passwd = NULL;

     char           error_buf[CURL_ERROR_SIZE];

     CURLcode   ret = FALSE;

     if(!cwmp_ctx || !pmem || !pcurl)

     {

         CWMP_LOG_ERROR(EVENT_MODULE, "some param is NULL\n");

         return FALSE;

     }

     curl = curl_easy_init();

     if (!curl)

     {

         CWMP_LOG_ERROR(EVENT_MODULE, "curl_easy_init fail\n");

         return FALSE;

     }

     memset(error_buf, , sizeof(error_buf));

     code = curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error_buf);

     if (code != CURLE_OK)

     {

         CWMP_LOG_ERROR(EVENT_MODULE, "Failed to set error buffer [%d]\n", code);

         return FALSE;

     }

     //设置回写函数

     code = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, cwmp_write_func_callback);

     if (code != CURLE_OK)

     {

         CWMP_LOG_ERROR(EVENT_MODULE, "Failed to set writer [%s]\n", curl_easy_strerror(code));

         goto finish;

     }

     code = curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)pmem);

     if (code != CURLE_OK)

     {

         CWMP_LOG_ERROR(EVENT_MODULE, "Failed to set write data [%s]\n", curl_easy_strerror(code));

         goto finish;

     }

     code = curl_easy_setopt(curl, CURLOPT_COOKIEFILE,  "");

     if (code != CURLE_OK)

     {

         CWMP_LOG_ERROR(EVENT_MODULE, "Failed to set cookie file [%s]\n", curl_easy_strerror(code));

         goto finish;

     }

     //设置鉴权参数

     code = curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC|CURLAUTH_DIGEST);

     if (code != CURLE_OK)

     {

         CWMP_LOG_ERROR(EVENT_MODULE, "Failed to set http auth [%s]\n", curl_easy_strerror(code));

         goto finish;

     }

     get_param_value_by_fullname(IGD_ManagementServer_Username, &acs_usr);

     get_param_value_by_fullname(IGD_ManagementServer_Password, &acs_passwd);

     if (acs_usr && acs_passwd)

     {

         //curl_easy_setopt(curl, CURLOPT_USERNAME, acs_usr);

         //curl_easy_setopt(curl, CURLOPT_PASSWORD, acs_passwd);

         code = curl_easy_setopt(curl, CURLOPT_USERNAME, acs_usr);

         if (code != CURLE_OK)

         {

             CWMP_LOG_ERROR(EVENT_MODULE, "Failed to set username [%s]\n", curl_easy_strerror(code));

             goto finish;

         }

         code = curl_easy_setopt(curl, CURLOPT_PASSWORD, acs_passwd);

         if (code != CURLE_OK)

         {

             CWMP_LOG_ERROR(EVENT_MODULE, "Failed to set password [%s]\n", curl_easy_strerror(code));

             goto finish;

         }

         CWMP_LOG_DEBUG(EVENT_MODULE, "acs usrname=%s, passwd=%s\n", acs_usr, acs_passwd);

     }

     else

     {

         CWMP_LOG_ERROR(EVENT_MODULE, "get acs usrname or passwd fail\n");

         goto finish;

     }

     code = curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, );

     if (code != CURLE_OK)

     {

         CWMP_LOG_ERROR(EVENT_MODULE, "Failed to set follow location [%s]\n", curl_easy_strerror(code));

         goto finish;

     }

     code = curl_easy_setopt(curl, CURLOPT_MAXREDIRS, );

     if (code != CURLE_OK)

     {

         CWMP_LOG_ERROR(EVENT_MODULE, "Failed to set max redirs [%s]\n", curl_easy_strerror(code));

         goto finish;

     }

     code = curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL);

     if (code != CURLE_OK)

     {

         CWMP_LOG_ERROR(EVENT_MODULE, "Failed to set POSTREDIR [%s]\n", curl_easy_strerror(code));

         goto finish;

     }

     // http timeout 30 seconds

     curl_easy_setopt(curl, CURLOPT_TIMEOUT, );   

     // not support SSL

     curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, );

     curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, );

     (*pcurl) = curl;

     ret = TRUE;

 finish:

     if(ret == FALSE)

     {

         if(curl)

         {

             curl_easy_cleanup(curl);

         }

     }

     if (acs_usr)

     {

         free_check(acs_usr);

     }

     if (acs_passwd)

     {

         free_check(acs_passwd);

     }

     return ret;

 }

2) CPE处理来自ACS的socket连接报文

 //处理socket连接

 static void *handle_sock(void *data)

 {

     Http_request    *request = NULL;

     int             sock = (int)(long)data;

     int             len = ;

     const char      *auth_str = NULL;

     int             auth = ;   //是否需要校验

     int             status = ;

     char            *usrname = NULL;

     char            *passwd = NULL;

     char            resp[MAX_BUF_LEN+] = {};

     char            auth_opaque[] = {};

     CWMP_LOG_ERROR(ACS_CONN_MODULE, "handle_sock begin, sock=%d\n", sock);

     len = http_parse_request(sock, &request);

     if(len == )

     {

         CWMP_LOG_ERROR(ACS_CONN_MODULE, "socket:%d is closed\n", sock);

         return NULL;

     }

     if(len < )

     {

         CWMP_LOG_INFO(ACS_CONN_MODULE, "read data finish\n");

         goto finish;

     }

     if(!request)

     {

         CWMP_LOG_ERROR(ACS_CONN_MODULE, "http_parse_request fail\n");

         goto finish;

     }

     //判断是否需要验证

     if(g_pcwmp_ctx->dev_info.func_get_auth)

     {

         auth = g_pcwmp_ctx->dev_info.func_get_auth();

     }

     CWMP_LOG_ERROR(ACS_CONN_MODULE, "auth=%d\n", auth);

     if(auth <= )   //不需要校验

     {

         status = ;

         goto response;

     }

     auth_str = http_header_get(request->header, "Authorization");

     if(!auth_str)

     {

         status = ;

         CWMP_LOG_ERROR(ACS_CONN_MODULE, "have not Authorization\n");

         goto response;

     }

     //校验

     get_param_value_by_fullname(IGD_ManagementServer_ConnectionRequestUsername, &usrname);

     get_param_value_by_fullname(IGD_ManagementServer_ConnectionRequestPassword, &passwd);

     if(!usrname || !passwd)

     {

         status = ;

     }

     if (check_digest_auth(auth_str, usrname, passwd) == FALSE)

     {

         status = ;       

     }

     else

     {

         status = ;

         CWMP_LOG_INFO(ACS_CONN_MODULE, "auth pass\n");

     }

 response:

     if(status == )

     {

         strcpy(resp, RESPONSE_200);

     }

     else if(status == )

     {

         strcpy(resp, RESPONSE_400);

     }

     else if(status == )

     {

         char buffer[] = {};

         char nonce[];

         g_auth_nonce++;

         snprintf(buffer, ,  "%d", g_auth_nonce);

         MD5(nonce, buffer, NULL);

         nonce[] = ;

         MD5(auth_opaque, g_auth_realm, NULL);

         snprintf(resp, MAX_BUF_LEN+, RESPONSE_401, g_auth_realm, "auth", nonce, auth_opaque);

     }

     //发送回应

     if(status != )

     {

         write_all(sock, resp, strlen(resp));

         CWMP_LOG_DEBUG(ACS_CONN_MODULE, "response to acs ok, status=%d\n", status);

     }

 finish:

     close(sock);

     if(request)

     {

         http_destroy_request(request);

     }

     if(usrname)

     {

         free_check(usrname);

     }

     if(passwd)

     {

         free_check(passwd);

     }

     if(status == )

     {

         //6 Connected Request加入事件队列      

         increase_event_set(EVENT_CONNECTIONREQUEST, );

         sem_post(&g_pcwmp_ctx->sem_send_acs);

     }

     CWMP_LOG_DEBUG(ACS_CONN_MODULE, "handle_sock end\n");

     return NULL;

 }

总结

TR069 协议采用SSL/TLS、HTTP basic或者HTTP digest等加密认证方式可以保证数据的安全性;采用较多Web中成熟的技术,实现简单,降低了开发难度;采用HTTP协议,可以有效地穿越复杂的网络环境。因此,TR069协议比较适合对广域网内的设备进行管理。

参考

1)

http://wenku.baidu.com/link?url=w0eT-wBfONNGajF3mVOUL_KUOvZAwXnIBw7B6mBj48ySO7vXE6M7xtOo48-NEn60Dpy1pST1ATqWGleyMdLIRogQryLB72n9PJZAd5znfS3

2)TR069规范

CWMP开源代码研究4——认证流程的更多相关文章

  1. CWMP开源代码研究——git代码工程

    原创作品,转载请注明出处,严禁非法转载.如有错误,请留言! email:40879506@qq.com 声明:本系列涉及的开源程序代码学习和研究,严禁用于商业目的. 如有任何问题,欢迎和我交流.(企鹅 ...

  2. CWMP开源代码研究5——CWMP程序设计思想

    声明:本文涉及的开源程序代码学习和研究,严禁用于商业目的. 如有任何问题,欢迎和我交流.(企鹅号:408797506) 本文介绍自己用过的ACS,其中包括开源版(提供下载包)和商业版(仅提供安装包下载 ...

  3. CWMP开源代码研究1——开篇之作

    原创作品,转载请注明出处,严禁非法转载.如有错误,请留言! email:40879506@qq.com 声明:本系列涉及的开源程序代码学习和研究,严禁用于商业目的. 如有任何问题,欢迎和我交流.(企鹅 ...

  4. CWMP开源代码研究2——easycwmp安装和学习

    声明:本文是对开源程序代码学习和研究,严禁用于商业目的. 如有任何问题,欢迎和我交流.(企鹅号:408797506) 本文所有笔记和代码可以到csdn下载:http://download.csdn.n ...

  5. CWMP开源代码研究3——ACS介绍

    声明:本文涉及的开源程序代码学习和研究,严禁用于商业目的. 如有任何问题,欢迎和我交流.(企鹅号:408797506) 本文介绍自己用过的ACS,其中包括开源版(提供下载包)和商业版(仅提供安装包下载 ...

  6. CWMP开源代码研究——cwmp移植

    原创作品,转载请注明出处,严禁非法转载.如有错误,请留言! email:40879506@qq.com 声明:本系列涉及的开源程序代码学习和研究,严禁用于商业目的. 如有任何问题,欢迎和我交流.(企鹅 ...

  7. CWMP开源代码研究6——libcwmp动态库开发

    原创作品,转载请注明出处,严禁非法转载.如有错误,请留言! email:40879506@qq.com 为了使程序具有通用性,便于扩展和维护.采用了"模块"插入的思想.将设备业务相 ...

  8. CWMP开源代码研究7——cwmp移植

    原创作品,转载请注明出处,严禁非法转载.如有错误,请留言! email:40879506@qq.com 声明:本系列涉及的开源程序代码学习和研究,严禁用于商业目的. 如有任何问题,欢迎和我交流.(企鹅 ...

  9. CWMP开源代码研究——stun的NAT穿透

    原创作品,转载请注明出处,严禁非法转载.如有错误,请留言! email:40879506@qq.com 参考: http://www.cnblogs.com/myblesh/p/6259765.htm ...

随机推荐

  1. ABP使用及框架解析系列 - [Unit of Work part.1-概念及使用]

    前言 ABP ABP是“ASP.NET Boilerplate Project”的简称. ABP的官方网站:http://www.aspnetboilerplate.com ABP在Github上的开 ...

  2. java-阻塞队列

    阻塞队列与普通队列的区别在于,当队列是空的时,从队列中获取元素的操作将会被阻塞,或者当队列是满时,往队列里添加元素的操作会被阻塞.试图从空的阻塞队列中获取元素的线程将会被阻塞,直到其他的线程往空的队列 ...

  3. ajax分页与组合查询配合使用

    使用纯HTML页与js.ajax.Linq实现分页与组合查询的配合使用 <body> <div id="top"><input type=" ...

  4. jQuery简单的手风琴菜单

    查看效果:http://keleyi.com/keleyi/phtml/menu/5.htm 本菜单的HTML代码和JS代码都简洁,完整源代码: <!DOCTYPE html PUBLIC &q ...

  5. javascript中的一些核心知识点以及需要注意的地方

    前言 近期杂事甚多,这些事情的积累对知识体系的提升有好处,但是却不能整理出来,也整理不出来 比如说我最近研究的Hybrid在线联调方案便过于依赖于业务,就算分享也不会有人读懂,若是抽一点来分享又意义不 ...

  6. iOS 支付宝的使用

    支付宝相关资源下载地址:支付宝开放平台  在移动支付功能处下载. 一.使用官方的Demo 需要配置基本信息: 打开“APViewController.m”文件,对以下三个参数进行编辑. 二.集成支付宝 ...

  7. Event 8306 5021 5059 5057发布安全令牌时异常

    现象:站点访问一个,应用程序池就自动停掉一个 日志报错       解决: 1.受域策略影响,作为批处理作业登录选项被锁定,需修改域策略: 更新后:     2.确保程序池账户倒在IIS_IUSER组 ...

  8. js获取网页高度

    网页可见区域宽: document.body.clientWidth网页可见区域高: document.body.clientHeight网页可见区域宽: document.body.offsetWi ...

  9. Android 手机卫士--确认密码对话框编写

    本文接着实现“确认密码”功能,也即是用户以前设置过密码,现在只需要输入确认密码 本文地址:http://www.cnblogs.com/wuyudong/p/5940718.html,转载请注明出处. ...

  10. @synchronized(self)的用法 小结

    @synchronized() 的作用是创建一个互斥锁,保证在同一时间内没有其它线程对self对象进行修改,起到线程的保护作用, 一般在公用变量的时候使用,如单例模式或者操作类的static变量中使用 ...