导语
有些设备没有做鉴权,有些操作不需要鉴权,使用Onvif协议时都应注意这些。
最开始使用的是OpenSSL,但觉得为了一个摘要加密使用这么大一个库,而且我要做跨平台,Windows直接把2个dll放在exe目录下即可,但对Linux不熟,配置OpenSSL很苦恼,于是想自己实现摘要加密。
实现
因自己有一个在Windows上实现了Onvif客户端操作的简单demo,里面使用的是OpenSSL,于是跟踪调试发现调用OpenSSL加密的函数在
wsseapi.cpp中
soap_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++两个版本。