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

你可能感兴趣的:(Onvif客户端与服务器通信时鉴权的自实现)