导语

有些设备没有做鉴权,有些操作不需要鉴权,使用Onvif协议时都应注意这些。
最开始使用的是OpenSSL,但觉得为了一个摘要加密使用这么大一个库,而且我要做跨平台,Windows直接把2个dll放在exe目录下即可,但对Linux不熟,配置OpenSSL很苦恼,于是想自己实现摘要加密。

实现

因自己有一个在Windows上实现了Onvif客户端操作的简单demo,里面使用的是OpenSSL,于是跟踪调试发现调用OpenSSL加密的函数在wsseapi.cppsoap_wsse_add_UsernameTokenDigest函数里的calc_digest,所以替换这个函数即可。这里感谢企鹅 609342205的指导!
当然soap_wsse_add_UsernameTokenDigest用到的其他函数也要一并拷贝到新文件里才能编译通过,摘要加密用的Secure Hashing Algorithm (SHA-1)。
下面是代码(接口保持原样):

OnvifDigest.h

/** SHA1 digest size in octets */
#define SOAP_SMD_SHA1_SIZE  (20)/** Size of the random nonce */
#define SOAP_WSSE_NONCELEN  (20)#define TOKEN_TIMESTAMP 5class COnvifDigest
{
public:COnvifDigest(const char * szUsername, const char * szPassword);~COnvifDigest(void);void Authentication(soap * pSoap, bool bAuth = true, const std::string strId = "");const char * GetUsername(void);const char * GetPassword(void);private:void TokenTimestmap(soap * pSoap, time_t lifetime = TOKEN_TIMESTAMP);void calc_nonce(struct soap *soap, char nonce[SOAP_WSSE_NONCELEN]);struct _wsse__Security* soap_wsse_add_Security(struct soap *soap);int  soap_wsse_add_UsernameTokenText(struct soap *soap, const char *id, const char *username, const char *password);int  soap_wsse_add_UsernameTokenDigest(struct soap *soap, const char *id, const char *username, const char *password);int  soap_wsse_add_Timestamp(struct soap *soap, const char *id, time_t lifetime);void calc_digest(struct soap *soap, const char *created, const char *nonce, int noncelen, const char *password, char hash[SOAP_SMD_SHA1_SIZE]);private:std::string m_strUsername;std::string m_strPassword;
};

OnvifDigest.cpp

#include "OnvifDigest.h"
#include "onvif/sha1.h"const char *wsse_PasswordTextURI = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText";
const char *wsse_PasswordDigestURI = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest";
const char *wsse_Base64BinaryURI = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary";COnvifDigest::COnvifDigest( const char * szUsername, const char * szPassword )
: m_strUsername(szUsername)
, m_strPassword(szPassword)
{}COnvifDigest::~COnvifDigest( void )
{}void COnvifDigest::Authentication( soap * pSoap, bool bAuth /*= true*/, const std::string strId /*= ""*/ )
{const char * szId = strId.empty() ? NULL : strId.c_str();if (bAuth){soap_wsse_add_UsernameTokenDigest(pSoap, szId, m_strUsername.c_str(), m_strPassword.c_str());TokenTimestmap(pSoap);}
}const char * COnvifDigest::GetUsername( void )
{return m_strUsername.c_str();
}const char * COnvifDigest::GetPassword( void )
{return m_strPassword.c_str();
}/* private */void COnvifDigest::TokenTimestmap( soap *pSoap, time_t lifetime /*= TOKEN_TIMESTAMP*/ )
{soap_wsse_add_Timestamp(pSoap, "Time", lifetime);
}void COnvifDigest::calc_nonce(struct soap *soap, char nonce[SOAP_WSSE_NONCELEN])
{int i;time_t r = time(NULL);memcpy(nonce, &r, 4);for (i = 4; i < SOAP_WSSE_NONCELEN; i += 4){ r = soap_random;memcpy(nonce + i, &r, 4);}
}struct _wsse__Security* COnvifDigest::soap_wsse_add_Security(struct soap *soap)
{/* if we don't have a SOAP Header, create one */soap_header(soap);/* if we don't have a wsse:Security element in the SOAP Header, create one */if (!soap->header->wsse__Security){ soap->header->wsse__Security = (_wsse__Security*)soap_malloc(soap, sizeof(_wsse__Security));soap_default__wsse__Security(soap, soap->header->wsse__Security);}return soap->header->wsse__Security;
}int COnvifDigest::soap_wsse_add_UsernameTokenText(struct soap *soap, const char *id, const char *username, const char *password)
{ _wsse__Security *security = soap_wsse_add_Security(soap);/* allocate a UsernameToken if we don't have one already */if (!security->UsernameToken)security->UsernameToken = (_wsse__UsernameToken*)soap_malloc(soap, sizeof(_wsse__UsernameToken));soap_default__wsse__UsernameToken(soap, security->UsernameToken);/* populate the UsernameToken */security->UsernameToken->wsu__Id = soap_strdup(soap, id);security->UsernameToken->Username = soap_strdup(soap, username);/* allocate and populate the Password */if (password){ security->UsernameToken->Password = (_wsse__Password*)soap_malloc(soap, sizeof(_wsse__Password));soap_default__wsse__Password(soap, security->UsernameToken->Password);security->UsernameToken->Password->Type = (char*)wsse_PasswordTextURI;security->UsernameToken->Password->__item = soap_strdup(soap, password);}return SOAP_OK;
}int COnvifDigest::soap_wsse_add_UsernameTokenDigest(struct soap *soap, const char *id, const char *username, const char *password)
{ _wsse__Security *security = soap_wsse_add_Security(soap);time_t now = time(NULL);const char *created = soap_dateTime2s(soap, now);char HA[SOAP_SMD_SHA1_SIZE], HABase64[29];char nonce[SOAP_WSSE_NONCELEN], *nonceBase64;/* generate a nonce */calc_nonce(soap, nonce);nonceBase64 = soap_s2base64(soap, (unsigned char*)nonce, NULL, SOAP_WSSE_NONCELEN);/* The specs are not clear: compute digest over binary nonce or base64 nonce? *//* compute SHA1(created, nonce, password) *//*  // boost计算结果不对,应该是我的使用方法不对unsigned int Digest[5] = {0};boost::uuids::detail::sha1   sha;sha.process_bytes(nonce, strlen(nonce));sha.process_bytes(created, strlen(created));sha.process_bytes(password, strlen(password));sha.get_digest(Digest);for (int n=0,i=0; n<SOAP_SMD_SHA1_SIZE; ){HA[n++] = (Digest[i] >> 24) & 0xFF;HA[n++] = (Digest[i] >> 16) & 0xFF;HA[n++] = (Digest[i] >> 8) & 0xFF;HA[n++] = Digest[i] & 0xFF;i++;}*/calc_digest(soap, created, nonce, SOAP_WSSE_NONCELEN, password, HA);
//  calc_digest(soap, created, nonce, SOAP_WSSE_NONCELEN, password, HA);/*calc_digest(soap, created, nonceBase64, strlen(nonceBase64), password, HA);*/soap_s2base64(soap, (unsigned char*)HA, HABase64, SOAP_SMD_SHA1_SIZE);/* populate the UsernameToken with digest */soap_wsse_add_UsernameTokenText(soap, id, username, HABase64);/* populate the remainder of the password, nonce, and created */security->UsernameToken->Password->Type = (char*)wsse_PasswordDigestURI;security->UsernameToken->Nonce = nonceBase64;security->UsernameToken->wsu__Created = soap_strdup(soap, created);return SOAP_OK;
}int COnvifDigest::soap_wsse_add_Timestamp( struct soap *soap, const char *id, time_t lifetime )
{_wsse__Security *security = soap_wsse_add_Security(soap);time_t now = time(NULL);char *created = soap_strdup(soap, soap_dateTime2s(soap, now));char *expired = lifetime ? soap_strdup(soap, soap_dateTime2s(soap, now + lifetime)) : NULL;/* allocate a Timestamp if we don't have one already */if (!security->wsu__Timestamp)security->wsu__Timestamp = (_wsu__Timestamp*)soap_malloc(soap, sizeof(_wsu__Timestamp));soap_default__wsu__Timestamp(soap, security->wsu__Timestamp);/* populate the wsu:Timestamp element */security->wsu__Timestamp->wsu__Id = soap_strdup(soap, id);security->wsu__Timestamp->Created = created;security->wsu__Timestamp->Expires = expired;return SOAP_OK;
}void COnvifDigest::calc_digest( struct soap *soap, const char *created, const char *nonce, int noncelen, const char *password, char hash[SOAP_SMD_SHA1_SIZE] )
{SHA1Context sha;SHA1Reset(&sha);SHA1Input(&sha, (unsigned char *)nonce, noncelen);SHA1Input(&sha, (unsigned char *)created, strlen(created));SHA1Input(&sha, (unsigned char *)password, strlen(password));if (!SHA1Result(&sha)){fprintf(stderr, "ERROR-- could not compute message digest\n");}else{int j = 0;for(int i = 0; i < 5 ; i++){hash[j++] = sha.Message_Digest[i] >> 24;hash[j++] = sha.Message_Digest[i] >> 16;hash[j++] = sha.Message_Digest[i] >> 8;hash[j++] = sha.Message_Digest[i] >> 0;}}
}

加密库:Secure Hashing Algorithm (SHA-1),有C、C++两个版本。

Onvif客户端与服务器通信时鉴权的自实现相关推荐

  1. 为什么获取crm服务器信息失败,为 Outlook 配置 Microsoft Dynamics CRM 客户端时出现 与 Microsoft Dynamics CRM 服务器通信时出现问题 错误...

    症状 当您尝试配置 microsoft Office Outlook 的 Microsoft Dynamics CRM 客户端时,收到以下错误消息: 与 Microsoft Dynamics CRM ...

  2. Onvif协议学习:7、鉴权认证

    Onvif协议学习:7.鉴权认证 文章目录 Onvif协议学习:7.鉴权认证 1.前言 2.ONVIF哪些接口需要认证 3.如何认证 4.安装OpenSSL 5.实现认证 6.特别注意 原文链接:ht ...

  3. Websocket(二)-客户端与服务器通信

    Websocket(二)-客户端与服务器通信 服务端 客户端测试 const WebSocket = require('ws'); const Server = WebSocket.Server; c ...

  4. 输入URL,客户端到服务器通信的过程

    输入URL,客户端到服务器通信全过程 按五层网络协议进行理解: 在主机上: 1.第五层--应用层:DNS解析 2.第四层--传输层:TCP三次握手.四次挥手 3.第三层--网络层:IP层 4.第2.5 ...

  5. 无法在Web服务器上启动调试。与Web服务器通信时出现身份验证错误

    使用Visual Studio 2005(Visual Studio 2008亦存在此问题)调试设置了主机头的网站时出现如下错误信息: --------- Microsoft Visual Studi ...

  6. 关于“与google服务器通信时出现问题“

    问题 Gmail等软件登录时不断核验设备,然后出现"与google服务器通信时出现问题"提示 解决方案 这个一般是应用设置上有问题 试着切换其他线路 全局设置的时候确认google ...

  7. Exchange邮件系统客户端与服务器通信常用网络端口

    Exchange邮件系统:客户端与服务器通信常用网络端口 序号 用途 端口 1 未加密的web连接: •互联网日历发布 •Outlook on the web(重定向到443/TCP) •自动发现(4 ...

  8. java socket5源码_Java利用TCP协议实现客户端与服务器通信【附通信源码】

    进行TCP协议网络程序的编写,关键在于ServerSocket套接字的熟练使用,TCP通信中所有的信息传输都是依托ServerSocket类的输入输出流进行的. TCP协议概念 先来了解一下TCP协议 ...

  9. RTSP安防网络摄像头/海康大华硬盘录像机网页无插件直播流媒体服务器EasyNVR之鉴权接口的调用配置说明

    进入移动互联网时代以来,企业微信公众号已成为除官网以外非常重要的宣传渠道,当3.2亿直播用户与9亿微信用户的势能累加,在微信上开启直播已成为越来越多企业的必然选择. EasyNVR核心在于摄像机的音视 ...

  10. 全网疯传!Java利用TCP协议实现客户端与服务器通信【附通信源码

    目录 TCP协议概念 ServerSocket类 服务器端程序 客户端程序 Hello!大家好哇!我是灰小猿! 上一篇博客和大家分享了在网络编程中要注意的基础知识,关于IP.TCP.UDP以及端口和套 ...

最新文章

  1. STM32控制OLCD显示中英文(NB-IoT专栏—基础篇6)
  2. Pycharm 社区版本Database Navigator 安装教程
  3. 失败的windows系统服务调用readfile():管道已结束?_操作系统之进程详解(一)
  4. matlab imfilter函数,Matlab的imfilter函数用法详解
  5. mysql数据库入门教程(14):函数
  6. JAX-RS 2.0:服务器端处理管道
  7. [react] 请说说什么是useEffect?
  8. 2020年10月抖音小红书美妆营销报告
  9. [Contest20180116]随机游走
  10. 向中级程序员转变的10个建议
  11. Delphi D10.X VCL和FireMonkey之间的常见差异介绍
  12. Java基础_week6
  13. 芭蕉树上第二十一根芭蕉-- matlab2017b安装的出现问题
  14. 区块链的概念定义是什么
  15. react-native-webrtc之采坑之旅
  16. 架构师速成4.4-我该学什么语言
  17. 【Swing】JTree:树组件
  18. firefly-rk3288开发板Linux驱动——LED驱动
  19. 一个控制键盘远程控制多台视频会议摄像机(转自搜狐)
  20. Cmpp MsgId 生成算法

热门文章

  1. delphi计算机语言排名,2020年3月TIOBE编程语言排行榜 Java继续蝉联榜首
  2. 国开大学计算机实操,国开大学计算机实操答案一 .pdf
  3. 使用python将视频按照帧转为图片
  4. java-net-php-python-jsp安利达物流公司管理系统计算机毕业设计程序
  5. 车牌识别算法实践(一):先验知识
  6. 搜索引擎提交软件_搜索引擎排名因素有哪些?
  7. OpenCL优化案例研究 (4)
  8. 个人邮箱与企业邮箱的区别
  9. Linux 程序 动态库 静态库依赖关系
  10. 计算机游戏测试软件,你的电脑能不能吃鸡,两款软件很简单就能测试出来