如何在C++中将RAND_bytes()返回值转为可存储的字符串?
这个问题我熟!你现在遇到的是二进制密钥存数据库的典型问题——因为直接把unsigned char数组转成string,里面包含大量非打印字符,SQLite存储时会出现截断、编码错误甚至数据丢失的情况。解决思路很简单:把二进制数据编码成可打印的ASCII字符串格式(比如十六进制或Base64),存进数据库,后续取出时再解码回原始二进制数据即可,完全不影响AES加密解密的正确性。
下面给你两种基于OpenSSL生态的实用实现方式,和你当前用的RAND_bytes完美兼容:
方法一:十六进制编码(直观易读)
十六进制编码会把每个字节转成两个0-9、A-F的ASCII字符,生成的字符串长度是原始二进制的2倍,优点是直观,方便手动检查。
编码函数(二进制转十六进制字符串)
#include <string> #include <cstdlib> // 用于strtol std::string bytes_to_hex(const unsigned char* bytes, size_t length) { std::string hex_str; hex_str.reserve(length * 2); // 预分配内存提升效率 const char hex_chars[] = "0123456789ABCDEF"; for (size_t i = 0; i < length; ++i) { unsigned char byte = bytes[i]; hex_str += hex_chars[byte >> 4]; // 取高4位 hex_str += hex_chars[byte & 0x0F]; // 取低4位 } return hex_str; }
使用示例(生成密钥并编码)
#include <openssl/rand.h> #include <iostream> int main() { unsigned char key_bytes[16]; if (RAND_bytes(key_bytes, sizeof(key_bytes)) != 1) { std::cerr << "生成随机密钥失败!" << std::endl; return 1; } std::string key_hex = bytes_to_hex(key_bytes, sizeof(key_bytes)); std::cout << "可存储的十六进制密钥:" << key_hex << std::endl; // 现在key_hex是纯ASCII字符串,直接存入SQLite即可 return 0; }
解码函数(十六进制字符串转二进制)
bool hex_to_bytes(const std::string& hex_str, unsigned char* bytes, size_t expected_length) { // 先检查长度是否匹配:十六进制长度应该是二进制长度的2倍 if (hex_str.length() != expected_length * 2) { return false; } for (size_t i = 0; i < expected_length; ++i) { std::string byte_str = hex_str.substr(i*2, 2); char* end_ptr; // 把十六进制字符串转成数值 long byte_val = std::strtol(byte_str.c_str(), &end_ptr, 16); // 检查转换是否成功,数值是否在0-255范围内 if (end_ptr != byte_str.c_str() + 2 || byte_val < 0 || byte_val > 255) { return false; } bytes[i] = static_cast<unsigned char>(byte_val); } return true; }
还原密钥示例(从数据库取出后解码)
#include <iostream> int main() { std::string stored_key_hex; // 假设从SQLite数据库读取到stored_key_hex unsigned char restored_key[16]; if (hex_to_bytes(stored_key_hex, restored_key, sizeof(restored_key))) { std::cout << "密钥还原成功,可以用于AES解密!" << std::endl; // 这里用restored_key进行解密操作 } else { std::cerr << "密钥解码失败!" << std::endl; return 1; } return 0; }
方法二:Base64编码(更紧凑)
Base64编码会把每3个字节转成4个ASCII字符,生成的字符串长度约为原始二进制的1.33倍,比十六进制更节省存储空间,适合大量密钥存储的场景。
编码函数(二进制转Base64字符串)
#include <string> #include <openssl/evp.h> std::string bytes_to_base64(const unsigned char* bytes, size_t length) { // 计算Base64编码后的长度 int base64_len = EVP_EncodeLength(length); std::string base64_str(base64_len, '\0'); // 使用OpenSSL内置的Base64编码函数 EVP_EncodeBlock(reinterpret_cast<unsigned char*>(&base64_str[0]), bytes, length); // 去掉EVP_EncodeBlock自动添加的换行符 size_t newline_pos = base64_str.find('\n'); if (newline_pos != std::string::npos) { base64_str.resize(newline_pos); } return base64_str; }
使用示例(生成密钥并编码)
#include <openssl/rand.h> #include <iostream> int main() { unsigned char key_bytes[16]; if (RAND_bytes(key_bytes, sizeof(key_bytes)) != 1) { std::cerr << "生成随机密钥失败!" << std::endl; return 1; } std::string key_base64 = bytes_to_base64(key_bytes, sizeof(key_bytes)); std::cout << "可存储的Base64密钥:" << key_base64 << std::endl; // 直接把key_base64存入SQLite即可 return 0; }
解码函数(Base64字符串转二进制)
#include <string> #include <openssl/evp.h> #include <cstring> // 用于memcpy bool base64_to_bytes(const std::string& base64_str, unsigned char* bytes, size_t& out_length) { // 计算解码后的最大可能长度 int decoded_max_len = EVP_DecodeLength(base64_str.length()); unsigned char decoded_buf[decoded_max_len]; // 使用OpenSSL内置的Base64解码函数 int actual_len = EVP_DecodeBlock(decoded_buf, reinterpret_cast<const unsigned char*>(base64_str.c_str()), base64_str.length()); if (actual_len < 0) { return false; } // 处理Base64的填充字符(=) if (base64_str.length() >= 2 && base64_str[base64_str.length()-2] == '=') { actual_len -= 2; } else if (base64_str.length() >= 1 && base64_str.back() == '=') { actual_len -= 1; } // 检查解码后的长度是否符合预期 if (actual_len > out_length) { return false; } std::memcpy(bytes, decoded_buf, actual_len); out_length = actual_len; return true; }
还原密钥示例(从数据库取出后解码)
#include <iostream> int main() { std::string stored_key_base64; // 假设从SQLite数据库读取到stored_key_base64 unsigned char restored_key[16]; size_t restored_len = sizeof(restored_key); if (base64_to_bytes(stored_key_base64, restored_key, restored_len) && restored_len == sizeof(restored_key)) { std::cout << "密钥还原成功,可以用于AES解密!" << std::endl; // 这里用restored_key进行解密操作 } else { std::cerr << "密钥解码失败!" << std::endl; return 1; } return 0; }
内容的提问来源于stack exchange,提问作者SayMyName




