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

导语

有些设备没有做鉴权,有些操作不需要鉴权,使用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 5

class 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++两个版本。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
实现一个 C# 的 Onvif 客户端,你需要遵循以下步骤: 1. 引用 Onvif 标准的 WSDL 文件 Onvif 标准定义了一组 Web Services Description Language (WSDL) 文件,你需要将这些文件添加到你的 C# 项目中。这些文件包含了 Onvif 标准所定义的服务接口和数据类型。 2. 生成客户端代理类 使用 Visual Studio 或者命令行工具,基于 WSDL 文件生成客户端代理类,这样你就可以使用 C# 代码调用 Onvif 标准定义的服务接口。 3. 创建客户端对象 使用生成的客户端代理类创建客户端对象,你需要提供 Onvif 设备的 IP 地址、端口号、用户名和密码等信息。 4. 调用服务接口 使用客户端对象调用 Onvif 标准定义的服务接口,例如获取设备信息、控制云台、获取视频流等操作。 下面是一个简单的示例代码,演示了如何使用 C# 实现 Onvif 客户端: ```csharp using System; using System.ServiceModel; using onvif.services; namespace onvif_client { class Program { static void Main(string[] args) { // Onvif 设备的 IP 地址、端口号、用户名和密码 string address = "http://192.168.1.100/onvif/device_service"; string username = "admin"; string password = "admin"; // 创建服务绑定和客户端对象 var binding = new BasicHttpBinding(); var endpoint = new EndpointAddress(address); var client = new DeviceClient(binding, endpoint); // 设置验证凭据 client.ClientCredentials.UserName.UserName = username; client.ClientCredentials.UserName.Password = password; try { // 调用服务接口 var response = client.GetDeviceInformation(new GetDeviceInformationRequest()); // 打印设备信息 Console.WriteLine("Manufacturer: {0}", response.Manufacturer); Console.WriteLine("Model: {0}", response.Model); Console.WriteLine("Serial Number: {0}", response.SerialNumber); } catch (Exception ex) { Console.WriteLine("Error: {0}", ex.Message); } finally { // 关闭客户端 client.Close(); } } } } ``` 以上示例代码演示了如何使用 Onvif 标准定义的 GetDeviceInformation 接口获取设备信息。你可以根据需要调用其他的服务接口,实现更加复杂的操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值