如何在C++中生成符合ONVIF规范的Password Digest?
正确生成ONVIF密码摘要的C++实现
首先明确ONVIF密码摘要的计算规则:
PasswordDigest = Base64编码( SHA1哈希( Base64解码(Nonce) + Date + Password ) )
其中:
- Nonce是Base64编码的随机二进制数据,不是明文字符串
- Date必须符合ISO 8601格式(如
2022-09-10T10:10:59Z,部分设备兼容带毫秒的格式,但建议用标准格式) - 拼接操作是字节级的直接合并,需确保字符串采用UTF-8编码
你的代码存在的问题
- Nonce处理错误:你直接对明文"secret"做Base64解码,而实际ONVIF的Nonce是设备返回的Base64格式字符串,需先获取该值再解码
- SHA1计算长度错误:
strlen(str.c_str()) - 1会丢弃最后一个字符,正确长度应为str.size()(std::string的size()返回实际字符数,不含终止符) - 日期格式风险:部分ONVIF设备不支持带毫秒的日期格式,可能导致验证失败
正确的C++实现(基于OpenSSL库)
以下是使用OpenSSL工具函数的可靠实现:
#include <iostream> #include <string> #include <openssl/sha.h> #include <openssl/bio.h> #include <openssl/evp.h> #include <openssl/buffer.h> // Base64解码:将Base64字符串转为原始字节 std::string base64_decode(const std::string& input) { BIO *bio, *b64; int decode_len = 0; char* buffer = nullptr; bio = BIO_new_mem_buf(input.data(), input.size()); b64 = BIO_new(BIO_f_base64()); bio = BIO_push(b64, bio); BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); // 忽略换行符 decode_len = BIO_pending(bio); buffer = new char[decode_len + 1]; decode_len = BIO_read(bio, buffer, decode_len); buffer[decode_len] = '\0'; std::string result(buffer, decode_len); delete[] buffer; BIO_free_all(bio); return result; } // Base64编码:将字节数组转为Base64字符串 std::string base64_encode(const unsigned char* input, size_t length) { BIO *bio, *b64; BUF_MEM *buffer_ptr; b64 = BIO_new(BIO_f_base64()); bio = BIO_new(BIO_s_mem()); bio = BIO_push(b64, bio); BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); // 不添加换行符 BIO_write(bio, input, length); BIO_flush(bio); BIO_get_mem_ptr(bio, &buffer_ptr); std::string result(buffer_ptr->data, buffer_ptr->length); BIO_free_all(bio); return result; } // 计算ONVIF密码摘要 std::string calculate_onvif_password_digest(const std::string& nonce_b64, const std::string& date, const std::string& password) { // 1. 解码Base64格式的Nonce std::string nonce_decoded = base64_decode(nonce_b64); // 2. 拼接字节流:解码后的Nonce + Date字符串 + Password字符串 std::string combined = nonce_decoded + date + password; // 3. 计算SHA1哈希 unsigned char sha1_hash[SHA_DIGEST_LENGTH]; SHA1(reinterpret_cast<const unsigned char*>(combined.data()), combined.size(), sha1_hash); // 4. 对哈希结果做Base64编码 return base64_encode(sha1_hash, SHA_DIGEST_LENGTH); } int main() { // 官方示例测试(验证正确性) std::string nonce_b64 = "LKqI6G/AikKCQrN0zqZFlg=="; std::string date = "2010-09-16T07:50:45Z"; std::string password = "userpassword"; std::string digest = calculate_onvif_password_digest(nonce_b64, date, password); std::cout << "官方示例摘要:" << digest << std::endl; // 应输出 tuOSpGlFlIXsozq4HFNeeGeFLEI= // 你的业务场景(替换为设备返回的Nonce) std::string your_nonce_b64 = "替换为设备返回的Base64格式Nonce"; std::string your_date = "2022-09-10T10:10:59Z"; std::string your_password = "mypassword"; std::string your_digest = calculate_onvif_password_digest(your_nonce_b64, your_date, your_password); std::cout << "你的场景摘要:" << your_digest << std::endl; return 0; }
编译与使用注意事项
- 编译时需链接OpenSSL库,例如g++命令:
g++ onvif_digest.cpp -o onvif_digest -lcrypto - Nonce必须使用设备在握手阶段返回的Base64值,不能自行指定明文
- Date必须与SOAP请求中
Created字段的时间完全一致,否则摘要验证会失败 - 确保Date、Password等字符串采用UTF-8编码,避免字符编码导致的验证错误
内容的提问来源于stack exchange,提问作者O'Connor
相关产品推荐
相关产品推荐

