海康摄像头http抓图

老版本的摄像头使用海康的协议http://user:password@192.168.1.64/ISAPI/Streaming/channels/33/picture
就能够取得图片。
而后新型号的摄像头http须要Digest authorization认证。没有认证的话只会返回401unauthorized错误:html

<!DOCTYPE html>
<html>

<head>
	<title>Document Error: Unauthorized</title>
</head>

<body>
	<h2>Access Error: 401 -- Unauthorized</h2>
	<p>Authentication Error</p>
</body>

</html>

curl 命令行解决401 unauthorized问题

在https://blog.csdn.net/bobchill/article/details/83302177 里博主用curl 命令行解决401 unauthorized问题,没想到真的能够用。我对应个人应用修改了命令:web

curl --insecure --anyauth -u admin:password -X GET http://admin:password@192.168.1.64/ISAPI/Streaming/channels/33/picture >b.jpeg

将接收结果直接输出成b.jpeg文件,确实是一张图片。
可是在openwrt中做用该命令却仍然返回401错误。也许是两个curl版本不一样吧。由于最终我仍是要经过libcurl编程来实现auth认证的,因此就不深究命令了。算法

HTTP Digest authentication的机制

是server收到client的HTTP request(INVITE),若是server须要客户端摘要认证,就须要生成一个摘要盘问(digest challenge),经过Response给client一个401 Unauthorized状态发送给用户
摘要盘问如 图二 中的WWW-Authenticate header所示:编程

在这里插入图片描述

摘要盘问中的各个参数意义以下:服务器

realm(领域):必须的,在全部的盘问中都必须有。它是目的是鉴别SIP消息中的机密。在实际应用中,它一般设置为server所负责的域名。curl

nonce (现时):必须的,这是由服务器规定的数据字符串,在服务器每次产生一个摘要盘问时,这个参数都是不同的(与前面所产生的不会雷同)。nonce 一般是由一些数据经过md5杂凑运算构造的。这样的数据一般包括时间标识和服务器的机密短语。确保每一个nonce 都有一个有限的生命期(也就是过了一些时间后会失效,而且之后不再会使用),并且是独一无二的(即任何其它的服务器都不能产生一个相同的nonce )。svg

Stale:没必要须,一个标志,用来指示客户端先前的请求因其nonce值过时而被拒绝。若是stale是TRUE(大小写敏感),客户端可能但愿用新的加密回应从新进行请求,而不用麻烦用户提供新的用户名和口令。服务器端只有在收到的请求nonce值不合法,而该nonce对应的摘要(digest)是合法的状况下(即客户端知道正确的用户名/口令),才能将stale置成TRUE值。若是stale是FALSE或其它非TRUE值,或者其stale域不存在,说明用户名、口令非法,要求输入新的值。测试

opaque(不透明体):必须的,这是一个不透明的(不让外人知道其意义)数据字符串,在盘问中发送给用户。加密

algorithm(算法):没必要须,这是用来计算杂凑的算法。当前只支持MD5算法。url

qop(保护的质量):必须的,这个参数规定服务器支持哪一种保护方案。客户端能够从列表中选择一个。值 “auth”表示只进行身份查验, “auth-int”表示进行查验外,还有一些完整性保护。须要看更详细的描述,请参阅RFC2617。

客户端反馈用户身份
client 生成 生成摘要响应(digest response),而后再次经过 http request (INVITE (Withink digest))发给 server。

摘要响应如 图三 中的Authenticate header所示:

在这里插入图片描述

摘要响应中的各个参数意义以下:

username: 不用再说明了
realm: 须要和 server 盘问的realm保持一致
nonce:客户端使用这个“现时”来产生摘要响应(digest response),须要和server 盘问中携带的nonce保持一致,这样服务器也会在一个摘要响应中收到“现时”的内容。服务器先要检查了“现时”的有效性后,才会检查摘要响应的其它部分。

于是,nonce 在本质上是一种标识符,确保收到的摘要机密,是从某个特定的摘要盘问产生的。还限制了摘要盘问的生命期,防止将来的重播攻击。

qop:客户端选择的保护方式。

nc (现时计数器):这是一个16进制的数值,即客户端发送出请求的数量(包括当前这个请求),这些请求都使用了当前请求中这个“现时”值。例如,对一个给定的“现时”值,在响应的第一个请求中,客户端将发送“nc=00000001”。这个指示值的目的,是让服务器保持这个计数器的一个副本,以便检测重复的请求。若是这个相同的值看到了两次,则这个请求是重复的。

response:这是由用户代理软件计算出的一个字符串,以证实用户知道口令。好比能够经过 username、password、http method、uri、以及nonce、qop等使用MD5加密生成。

cnonce:这也是一个不透明的字符串值,由客户端提供,而且客户端和服务器都会使用,以免用明文文本。这使得双方均可以查验对方的身份,并对消息的完整性提供一些保护。

uri:这个参数包含了客户端想要访问的URI。

server 确认用户
确认用户主要由两部分构成:
检查nonce的有效性
检查摘要响应中的其余信息, 好比server能够按照和客户端一样的算法生成一个response值,和client传递的response进行对比。
————————————————
以上出自原文连接:https://blog.csdn.net/andrewpj/article/details/45727853

费话很少说,开干吧,如何经过c语言的libcurl实现呢

c语言的libcurl实现HTTP Digest authentication

先写一个HTTP request(INVITE)请求:

int DigestAuthenticate(void)
{
    char *buffer,*pb;
    buffer=(char*)calloc(1024,sizeof(char));
    memset(buffer,0,1024);
    pb=buffer;
    CURL *hnd = curl_easy_init();

    curl_easy_setopt(hnd, CURLOPT_CUSTOMREQUEST, "GET");
    curl_easy_setopt(hnd, CURLOPT_URL, "http://192.168.1.64/ISAPI/Streaming/channels/33/picture");

    curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYPEER, false);
    curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYHOST, false);
    //curl_easy_setopt(hnd, CURLOPT_RETURNTRANSFER, 1);
    curl_easy_setopt(hnd, CURLOPT_FOLLOWLOCATION, false);
    curl_easy_setopt(hnd, CURLOPT_TIMEOUT, 30);
    curl_easy_setopt(hnd, CURLOPT_CONNECTTIMEOUT, 30);
    curl_easy_setopt(hnd, CURLOPT_HEADER, 1);

    curl_easy_setopt(hnd, CURLOPT_WRITEFUNCTION, read_data_curl);
    curl_easy_setopt(hnd, CURLOPT_WRITEDATA, buffer);

    CURLcode ret = curl_easy_perform(hnd);

    if(CURLE_OK == ret) {
       printf("\n%s\n",buffer);
    }
    else{
        curl_easy_cleanup(hnd);
        free(pb);
        printf("ret:%d\n",ret);
        return -1;
    }
    curl_easy_cleanup(hnd);
    printf("---------------------end----------------------\n");
    free(pb);
    return 1;
}

输出结果:

HTTP/1.1 401 Unauthorized
Date: Thu, 31 Oct 2019 12:46:06 GMT
Server: webserver
X-Frame-Options: SAMEORIGIN
Content-Length: 178
Content-Type: text/html
Connection: close
WWW-Authenticate: Digest qop="auth", realm="IP Camera(D6153)", nonce="597a553359574d334e546f794d5752684d44526c5a673d3d", stale="FALSE"

<!DOCTYPE html>
<html><head><title>Document Error: Unauthorized</title></head>
<body><h2>Access Error: 401 -- Unauthorized</h2>
<p>Authentication Error</p>
</body>
</html>

WWW-Authenticate就是咱们须要的东西,接着用它来计算出其它参数。

//这里实现Digest Auth逻辑
String A1 = null; //A1 = MD5("usarname:realm:password");
String A2 = null; //A2 = MD5("httpmethod:uri");
String response = null; //response = MD5("A1:nonce:nc:cnonce:qop:A2");

计算response是个难道。用一个方法能够验证response算得对不对。首先看一下Postman测试中获取的过程,其它的不要,咱们看Authorization: Digest 后的字符串,就是此次auth的各个参数:

在这里插入图片描述
在https://www.cnblogs.com/johnw/p/5487447.html介绍使用lLinux下使用openss的MD5加密BASE64加密方法。好东西呀,这一下不须要等编程就能够验证计算MD5对不对了。
在这里插入图片描述 能够看到结果和上面Postman的response是同样的,证实上面说的Digest Auth逻辑是对的。