使用OpenSSL生成HMAC token时出现未知段错误的求助
解决OpenSSL HMAC()调用段错误问题
问题背景
尝试为Mongoose WS添加自定义用户访问令牌,使用OpenSSL库的HMAC函数生成验证哈希,但调用HMAC()时持续出现段错误。已验证密钥、令牌的长度有效性,通过backtrace定位错误未取得明显进展。
相关代码如下:
#include "token.h" #define TK_SIZE 32 #define HASH_SIZE 32 #define PKEY_PATH "mysecretkey.pem" int get_pkey(unsigned char **key, size_t *key_len) { FILE* pkey_file = fopen(PKEY_PATH, "r"); if (!pkey_file) return 0; fseek(pkey_file, 0, SEEK_END); *key_len = ftell(pkey_file); fseek(pkey_file, 0, SEEK_SET); *key = (unsigned char*) malloc(*key_len); if (!(*key)) { fclose(pkey_file); return 0; } if (fread(*key, 1, *key_len, pkey_file) != *key_len) { free(*key); fclose(pkey_file); return 0; } fclose(pkey_file); return 1; } int get_tk_hash(char** tk_hash) { unsigned char tk[TK_SIZE]; unsigned char hash[HASH_SIZE]; unsigned char* key = NULL; size_t key_len = 0; unsigned int hashlen = HASH_SIZE; // 获取密钥 if (!get_pkey(&key, &key_len)) goto err; // 生成32字节令牌 if (!gen_32byte_token(tk)) goto err; // 生成令牌的HMAC哈希 if (!HMAC(EVP_sha256(), key, key_len, tk, TK_SIZE, hash, &hashlen)) goto err; // 将令牌和哈希转换为十六进制 char tk_to_hex[TK_SIZE * 2 + 1]; uchar_to_hex(tk, TK_SIZE, tk_to_hex); char hash_to_hex[HASH_SIZE * 2 + 1]; uchar_to_hex(hash, HASH_SIZE, hash_to_hex); // 组合令牌+哈希,用点分隔 char hex[sizeof(tk_to_hex) + 1 + sizeof(hash_to_hex)] = ""; strcat(hex, tk_to_hex); strcat(hex, "."); strcat(hex, hash_to_hex); *tk_hash = (char*) malloc(strlen(hex) + 1); if (*tk_hash == NULL) goto err; strcpy(*tk_hash, hex); free(key); // 使用后释放密钥 return 1; err: if (key) free(key); return 0; } int gen_32byte_token(unsigned char* tk) { if (RAND_bytes(tk, TK_SIZE) != 1) { return 0; } return 1; }
错误原因分析
- 密钥读取方式错误:HMAC需要的是原始字节流形式的对称密钥,但当前代码直接读取整个PEM格式文件的内容,包含
-----BEGIN PRIVATE KEY-----这类文本头、尾以及换行符,这些额外字符会被当作密钥的一部分传入HMAC函数,可能导致内部内存访问错误。 - OpenSSL未初始化:未调用OpenSSL的初始化函数(如
OPENSSL_init_ssl()或OPENSSL_add_all_algorithms()),会导致EVP_sha256()返回空指针,调用HMAC()时使用空指针必然引发段错误。 - 局部数组未完全初始化:
char hex[sizeof(tk_to_hex) + 1 + sizeof(hash_to_hex)] = "";仅将数组第一个元素设为\0,其余元素为未初始化的垃圾值,虽然strcat会从\0开始追加,但存在潜在的内存安全风险,极端情况下可能干扰后续操作。
修复方案
1. 正确读取HMAC密钥
如果使用的是对称密钥,建议直接存储原始字节(而非PEM格式),修改get_pkey函数用二进制模式读取:
int get_pkey(unsigned char **key, size_t *key_len) { FILE* pkey_file = fopen(PKEY_PATH, "rb"); // 二进制模式读取避免换行符转换 if (!pkey_file) return 0; fseek(pkey_file, 0, SEEK_END); *key_len = ftell(pkey_file); fseek(pkey_file, 0, SEEK_SET); *key = (unsigned char*) malloc(*key_len); if (!(*key)) { fclose(pkey_file); return 0; } if (fread(*key, 1, *key_len, pkey_file) != *key_len) { free(*key); fclose(pkey_file); return 0; } fclose(pkey_file); return 1; }
若必须使用PEM格式,需解析去掉头尾文本和换行,提取原始密钥字节(注意HMAC不需要非对称私钥,建议改用对称密钥存储)。
2. 初始化OpenSSL
在程序入口处添加OpenSSL初始化代码:
#include <openssl/ssl.h> int main() { // 初始化OpenSSL算法和字符串 OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL); OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL); // 后续业务代码... }
3. 修正局部数组初始化
将hex数组完全初始化为0,避免未初始化内存问题:
char hex[sizeof(tk_to_hex) + 1 + sizeof(hash_to_hex)]; memset(hex, 0, sizeof(hex)); strcat(hex, tk_to_hex); strcat(hex, "."); strcat(hex, hash_to_hex);
4. 验证EVP_sha256()返回值
在调用HMAC()前检查哈希算法对象是否有效:
const EVP_MD* md = EVP_sha256(); if (!md) goto err; if (!HMAC(md, key, key_len, tk, TK_SIZE, hash, &hashlen)) goto err;
修正后的关键代码片段
// 程序入口初始化 #include <openssl/ssl.h> int main() { OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS, NULL); OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL); // 调用get_tk_hash生成令牌... } // 修正后的get_tk_hash函数 int get_tk_hash(char** tk_hash) { unsigned char tk[TK_SIZE]; unsigned char hash[HASH_SIZE]; unsigned char* key = NULL; size_t key_len = 0; unsigned int hashlen = HASH_SIZE; if (!get_pkey(&key, &key_len)) goto err; if (!gen_32byte_token(tk)) goto err; const EVP_MD* md = EVP_sha256(); if (!md) goto err; if (!HMAC(md, key, key_len, tk, TK_SIZE, hash, &hashlen)) goto err; char tk_to_hex[TK_SIZE * 2 + 1]; uchar_to_hex(tk, TK_SIZE, tk_to_hex); char hash_to_hex[HASH_SIZE * 2 + 1]; uchar_to_hex(hash, HASH_SIZE, hash_to_hex); char hex[sizeof(tk_to_hex) + 1 + sizeof(hash_to_hex)]; memset(hex, 0, sizeof(hex)); strcat(hex, tk_to_hex); strcat(hex, "."); strcat(hex, hash_to_hex); *tk_hash = (char*) malloc(strlen(hex) + 1); if (*tk_hash == NULL) goto err; strcpy(*tk_hash, hex); free(key); return 1; err: if (key) free(key); return 0; }
内容的提问来源于stack exchange,提问作者kliam
相关产品推荐
相关产品推荐

