You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在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

火山引擎 最新活动