场景

  • 大华摄像头想实现控制代码,搜索来了很多,找到了官网的sdk,无奈支持 linux、windows,使用mac不能使用lib、dll 库。 终于找到了 https://github.com/nayrnet/node-dahua-api 但是已经是很久之前的了,clone下来本地下载下来发现无法使用,高版本nodejs直接就报错,使用nvm 切换到10.16.3才运行起来。 尝试连接线上的摄像头发现无法使用,返回 401 鉴权不通过。

代码自己更改

  • 通过issues 找到了回复 https://github.com/nayrnet/node-dahua-api/issues/9 最先版本的大华摄像头不能使用basic认证了,修改为了Digiest
  • digiest相关参考
    • https://www.cnblogs.com/lsdb/p/10621940.html

basic和digest区别

basic 认证形式

  • 请求示例如下,主要是Authorization头(位置不重要,http头一般都不分先后)

  • GET /GetDeviceInfo HTTP/1.1
    Host: 192.168.220.128
    Authorization: Basic YWRtaW46MTIzNDU2
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:60.0) Gecko/20100101 Firefox/60.0
    Accept-Encoding: gzip, deflate
    Accept: */*
    Cache-Control: no-cache
    Cookie: Secure
    Connection: close
    
  • basic认证方式(前边请求Authorization头的YWRtaW46MTIzNDU2,实际上是用户名admin密码123456使用以下计算方法得到)

  • var authHeader =
         "Basic " + new Buffer(options.user + ":" + options.pass).toString("base64");
    

gigest 认证形式

  • 请求直接返回401 内容为

    • // 主要需要的内容是 WWW-Authenticate  服务器根据请求自动生成的
      const r = [
        "HTTP/1.1 401 Unauthorized",
        'WWW-Authenticate: Digest realm="Login to f5c8557f6bee8548b0d72a1c30780e5e", qop="auth", nonce="1189347886", opaque="d6ae186150aa54aa82e816e281102905e064907f"',
        "Connection: close",
        "Set-Cookie:secure; HttpOnly",
        "CONTENT-LENGTH: 0",
        "",
        "",
      ];
      
  • 拿到返回值 根据特定组合编码放置下次请求 Authorization

    GET /GetDeviceInfo HTTP/1.1
    Host: 192.168.220.128
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:60.0) Gecko/20100101 Firefox/60.0
    Authorization: Digest username="admin",realm="TVT API Test Tool",nonce="d4f95e85dc5a39a4914db61b67878f5b",uri="GetDeviceInfo",algorithm="MD5",cnonce="d4f95e85dc5a39a4914db61b67878f5b",nc=00000001,qop="auth",response="1cc4cf126d3c4a70d2de34c5d8c2943c"
    Accept-Encoding: gzip, deflate
    Accept: */*
    Cache-Control: no-cache
    Cookie: Secure
    Connection: close
    
  • // 注意  - 是返回值有的   - XXX 是需要自己处理的值
    
    // username----系统用户名;客户端自行填充 - admin
    
    // realm----领域;服务端通过WWW-Authenticate头返回内容可以自己随便定,但其目的是用于提示客户端当前是什么系统,所以规范来说应类似于“myhost@testrealm.com”的形式。  -
    
    // nonce----服务端通过WWW-Authenticate头返回的随机数 -
    
    // uri----请求接口或资源(似乎规范来说应用GET或POST后的一样,上边例子中少了/是因为服务端没按规范实现)- GetDeviceInfo
    
    // algorithm----后边response用的计算方法 - MD5
    
    // cnonce----client nonce,客户端生成的随机数 - uuid
    
    // nc----nonce count,用于标识进行请求的次数。(但你一直不变服务端也不会管你对不对)- 00000001
    
    // qop----quality of protection,进一步限定response的计算方法,服务端通过WWW-Authenticate头返回。 -
    
    // response----认证最主要的值,前面各字段除algorithm外全要参与该值的计算。
    
  • 认证计算方式

    • 在最开始的RFC 2069中规定response计算方法如下:

    • HA1 = MD5(username:realm:password)
      HA2 = MD5(method:uri)
      response = MD5(HA1:nonce:HA2)
      
    • 随后的RFC 2617对计算方法进行了增强,规定计算方法如下(当algorithm值为MD5或未指定、qop未指定时等同RFC 2069

    • # HA1部分
      # 当algorithm值为"MD5"或未指定时,HA1计算方法如下
      HA1 = MD5(username:realm:password)
      # 当algorithm值为"MD5-sess"时,HA1计算方法如下
      HA1 = MD5(MD5(username:realm:password):nonce:cnonce)
      
      # HA2部分
      # 当qop值为"auth"或未指定时,HA2计算方法如下
      HA2 = MD5(method:uri)
      # 当qop值为"auth-int"时,HA2计算方法如下;entityBody是指整个body(?)
      HA2 = MD5(method:uri:MD5(entityBody))
      
      # response部分
      # 当qop值为"auth""auth-int"时,response计算方法如下
      response = MD5(HA1:nonce:nonceCount:cnonce:qop:HA2)
      # 当qop未指定时,response计算方法如下
      response = MD5(HA1:nonce:HA2)
      
    • nodejs 编写测试文件 测试组合

    • /*
       * @Author: 周凯
       * @Date: 2020-10-20 14:31:24
       * @LastEditTime: 2020-10-20 15:46:06
       */
      const r = [
        "HTTP/1.1 401 Unauthorized",
        'WWW-Authenticate: Digest realm="Login to f5c8557f6bee8548b0d72a1c30780e5e", qop="auth", nonce="1189347886", opaque="d6ae186150aa54aa82e816e281102905e064907f"',
        "Connection: close",
        "Set-Cookie:secure; HttpOnly",
        "CONTENT-LENGTH: 0",
        "",
        "",
      ];
      const md5 = require("md5");
      const { v4 } = require("uuid");
      
      const data = r[1];
      
      console.log(generateAuthorization(data, "admin", "a12345678"));
      function generateAuthorization(data, username, password) {
        // 最终结果值
        const endParams = {
          realm: "",
          qop: "",
          nonce: "",
          opaque: "",
          cnonce: v4().replace(/-/gi, ""),
          nc: "00000001",
        };
        console.log("data", data);
        console.log("\n");
        const rr = data.replace("WWW-Authenticate", "Authenticate");
      
        rr.split(",").forEach((item) => {
          let r = item.trim().replace(/=/gi, ":");
          if (foramtParams(r, "realm:")) endParams.realm = foramtParams(r, "realm:");
          if (foramtParams(r, "qop:")) endParams.qop = foramtParams(r, "qop:");
          if (foramtParams(r, "nonce:")) endParams.nonce = foramtParams(r, "nonce:");
          if (foramtParams(r, "opaque:"))
            endParams.opaque = foramtParams(r, "opaque:");
        });
        /**
         * 获取参数时的处理
         * @param {*} data 数据
         * @param {*} val 选择搜索的值
         */
        function foramtParams(data, val) {
          if (data.indexOf(val) !== -1 || data.indexOf(val) === 0) {
            return data.substring(data.indexOf(val) + val.length).replace(/"/gi, "");
          }
          return false;
        }
      
        // HA1部分
        const HA1 = md5(`${username}:${endParams.realm}:${password}`);
      
        // HA2
        const HA2 = md5("GET:GetDeviceInfo");
        const response = md5(
          `${HA1}:${endParams.nonce}:${endParams.nc}:${endParams.cnonce}:${endParams.qop}:${HA2}`
        );
        const returnValidationHeader = `Authorization: Digest username="${username}",realm="${endParams.realm}",nonce="${endParams.nonce}",uri="GetDeviceInfo",algorithm="MD5",cnonce="${endParams.cnonce}",nc="${endParams.nc}",qop="${endParams.qop}",response="${response}"`;
        return returnValidationHeader;
      }
      

更多查看

  • 直接下载npm包使用 npm install node-dahua-api-zhoukai
    • 仓库地址 https://gitee.com/EightDoor/dahua-api-news
  • demo 示例: https://gitee.com/EightDoor/dahua-api-demo
  • 大华技术白皮书
  • 大华api文档

结语

  • 功能目前实现了 摄像头的控制(ptzMove),其他部分正在不停完善中

感谢

  • https://github.com/nayrnet/node-dahua-api
Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐