当前位置: 首页 > news >正文

OpenSSL 文件验签与字符串验签原理及 C 语言实现详解

OpenSSL 文件验签与字符串验签原理及 C 语言实现详解

摘要

本文深入探讨了基于 OpenSSL 的文件验签与字符串验签原理及 C 语言实现方法。先阐述数字签名验证原理,包括基于非对称加密算法和哈希算法的签名生成与验证流程,着重介绍 OpenSSL 中利用 EVP 高级接口实现数字签名验证的关键函数及其作用。随后分别提供文件验签和字符串验签的完整 C 语言代码示例,详细解析各步骤要点,如公钥加载、数据读取、验证上下文初始化、更新验证数据及完成验证等,并给出代码步骤详解。此外,还涵盖错误处理与调试技巧,通过 OpenSSL 错误报告机制排查验证失败原因,列举常见错误类型及处理方式。同时对比文件验签与字符串验签特性,从数据来源、加载方式、大数据处理、适用场景及性能角度分析差异。最后总结最佳实践,涵盖算法选择、密钥管理、错误处理、资源管理和性能优化等方面,助力开发者在 OpenSSL 中高效实现文件和字符串签名验证功能,保障数据完整性和真实性。

数字签名验证原理

签名生成原理

  • 哈希计算 :对原始数据使用哈希算法(如 SHA - 256)生成固定长度的消息摘要。
  • 私钥加密 :使用签名者的私钥对消息摘要进行加密,生成数字签名。
  • 签名存储 :将数字签名与原始数据一起存储或传输。

签名验证原理

  • 哈希计算 :对接收到的原始数据使用相同的哈希算法生成消息摘要。
  • 公钥解密 :使用签名者的公钥对数字签名进行解密,得到原始消息摘要。
  • 摘要比对 :比较计算得到的消息摘要和解密得到的消息摘要,如果一致则验证通过。

在 OpenSSL 中,借助 EVP(Envelope)高级接口实现上述流程,主要涉及以下函数:

  • EVP_DigestVerifyInit() :初始化验证上下文。
  • EVP_DigestVerifyUpdate() :输入待验证数据。
  • EVP_DigestVerifyFinal() :完成验证并返回结果。

文件验签 C 语言实现

以下是使用 OpenSSL EVP 接口实现文件验签的完整 C 代码示例:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/err.h>// 验证文件签名
int verify_file_signature(const char* pubkey_file, const char* data_file,const char* sig_file) {EVP_MD_CTX* mdctx = NULL;EVP_PKEY* pubkey = NULL;FILE* pubkey_fp = NULL;FILE* data_fp = NULL;FILE* sig_fp = NULL;unsigned char* data = NULL;unsigned char* sig = NULL;size_t data_len = 0;size_t sig_len = 0;int result = -1;// 1. 读取公钥文件pubkey_fp = fopen(pubkey_file, "r");if (!pubkey_fp) {fprintf(stderr, "Error opening public key file\n");goto cleanup;}pubkey = PEM_read_PUBKEY(pubkey_fp, NULL, NULL, NULL);if (!pubkey) {fprintf(stderr, "Error reading public key\n");goto cleanup;}// 2. 读取数据文件data_fp = fopen(data_file, "rb");if (!data_fp) {fprintf(stderr, "Error opening data file\n");goto cleanup;}fseek(data_fp, 0, SEEK_END);data_len = ftell(data_fp);fseek(data_fp, 0, SEEK_SET);data = (unsigned char*)malloc(data_len);if (!data) {fprintf(stderr, "Memory allocation error\n");goto cleanup;}if (fread(data, 1, data_len, data_fp) != data_len) {fprintf(stderr, "Error reading data file\n");goto cleanup;}// 3. 读取签名文件sig_fp = fopen(sig_file, "rb");if (!sig_fp) {fprintf(stderr, "Error opening signature file\n");goto cleanup;}fseek(sig_fp, 0, SEEK_END);sig_len = ftell(sig_fp);fseek(sig_fp, 0, SEEK_SET);sig = (unsigned char*)malloc(sig_len);if (!sig) {fprintf(stderr, "Memory allocation error\n");goto cleanup;}if (fread(sig, 1, sig_len, sig_fp) != sig_len) {fprintf(stderr, "Error reading signature file\n");goto cleanup;}// 4. 初始化验证上下文mdctx = EVP_MD_CTX_new();if (!mdctx) {fprintf(stderr, "Error creating EVP_MD_CTX\n");goto cleanup;}if (EVP_DigestVerifyInit(mdctx, NULL, EVP_sha256(), NULL, pubkey) != 1) {fprintf(stderr, "Error initializing verification\n");goto cleanup;}// 5. 更新验证数据if (EVP_DigestVerifyUpdate(mdctx, data, data_len) != 1) {fprintf(stderr, "Error updating verification data\n");goto cleanup;}// 6. 完成验证result = EVP_DigestVerifyFinal(mdctx, sig, sig_len);if (result == 1) {printf("Signature verification successful\n");} else if (result == 0) {printf("Signature verification failed\n");} else {fprintf(stderr, "Error during verification\n");result = -1;}cleanup:// 7. 清理资源if (mdctx) EVP_MD_CTX_free(mdctx);if (pubkey) EVP_PKEY_free(pubkey);if (pubkey_fp) fclose(pubkey_fp);if (data_fp) fclose(data_fp);if (sig_fp) fclose(sig_fp);if (data) free(data);if (sig) free(sig);return result;
}int main(int argc, char* argv[]) {if (argc != 4) {printf("Usage: %s <public_key.pem> <data_file> <signature_file>\n", argv[0]);return 1;}return verify_file_signature(argv[1], argv[2], argv[3]) != 1;
}

代码步骤详解

  1. 加载公钥 :利用 PEM_read_PUBKEY 从 PEM 格式的公钥文件中读取公钥。
  2. 读取数据文件 :将待验证的文件内容完整读入内存缓冲区。
  3. 读取签名文件 :把签名数据读入内存缓冲区。
  4. 初始化验证上下文 :创建 EVP_MD_CTX 结构,并借助 EVP_DigestVerifyInit 进行初始化。
  5. 更新验证数据 :调用 EVP_DigestVerifyUpdate 将文件数据输入验证过程。
  6. 完成验证 :通过 EVP_DigestVerifyFinal 进行最终验证。
  7. 资源清理 :释放所有分配的内存和文件资源。

字符串验签 C 语言实现

字符串验签与文件验签主要区别在于数据来源不同,以下是处理内存中字符串数据的验签实现:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/err.h>// 验证字符串签名
int verify_string_signature(const char* pubkey_file, const char* message,const unsigned char* sig,size_t sig_len) {EVP_MD_CTX* mdctx = NULL;EVP_PKEY* pubkey = NULL;FILE* pubkey_fp = NULL;int result = -1;// 1. 读取公钥文件pubkey_fp = fopen(pubkey_file, "r");if (!pubkey_fp) {fprintf(stderr, "Error opening public key file\n");goto cleanup;}pubkey = PEM_read_PUBKEY(pubkey_fp, NULL, NULL, NULL);if (!pubkey) {fprintf(stderr, "Error reading public key\n");goto cleanup;}// 2. 初始化验证上下文mdctx = EVP_MD_CTX_new();if (!mdctx) {fprintf(stderr, "Error creating EVP_MD_CTX\n");goto cleanup;}if (EVP_DigestVerifyInit(mdctx, NULL, EVP_sha256(), NULL, pubkey) != 1) {fprintf(stderr, "Error initializing verification\n");goto cleanup;}// 3. 更新验证数据(直接使用字符串)if (EVP_DigestVerifyUpdate(mdctx, message, strlen(message)) != 1) {fprintf(stderr, "Error updating verification data\n");goto cleanup;}// 4. 完成验证result = EVP_DigestVerifyFinal(mdctx, sig, sig_len);if (result == 1) {printf("Signature verification successful\n");} else if (result == 0) {printf("Signature verification failed\n");} else {fprintf(stderr, "Error during verification\n");result = -1;}cleanup:// 5. 清理资源if (mdctx) EVP_MD_CTX_free(mdctx);if (pubkey) EVP_PKEY_free(pubkey);if (pubkey_fp) fclose(pubkey_fp);return result;
}// Base64解码函数
int base64_decode(const char* encoded, unsigned char** decoded, size_t* decoded_len) {BIO* bio = NULL;BIO* b64 = NULL;int len = strlen(encoded);*decoded = (unsigned char*)malloc(len);if (!*decoded) {return -1;}b64 = BIO_new(BIO_f_base64());bio = BIO_new_mem_buf((void*)encoded, len);bio = BIO_push(b64, bio);BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL);*decoded_len = BIO_read(bio, *decoded, len);BIO_free_all(bio);return (*decoded_len > 0) ? 0 : -1;
}int main() {const char* pubkey_file = "public_key.pem";const char* message = "This is a test message to verify";const char* base64_sig = "MEUCIQD..."; // Base64编码的签名unsigned char* sig = NULL;size_t sig_len = 0;// 解码Base64签名if (base64_decode(base64_sig, &sig, &sig_len) != 0) {fprintf(stderr, "Error decoding base64 signature\n");return 1;}int ret = verify_string_signature(pubkey_file, message, sig, sig_len);free(sig);return ret != 1;
}

代码步骤详解

  1. 加载公钥 :与文件验签相同,运用 PEM_read_PUBKEY 加载公钥。
  2. 初始化验证上下文 :创建并初始化 EVP_MD_CTX 结构。
  3. 更新验证数据 :直接使用字符串数据调用 EVP_DigestVerifyUpdate
  4. Base64 解码 :提供辅助函数处理 Base64 编码的签名数据。
  5. 完成验证 :调用 EVP_DigestVerifyFinal 进行最终验证。

错误处理与调试

OpenSSL 提供详细的错误报告机制,可在验证失败时获取更多信息:

void print_openssl_error() {unsigned long err;const char* file = NULL;const char* data = NULL;int line, flags;while ((err = ERR_get_error_line_data(&file, &line, &data, &flags)) != 0) {char buf[256];ERR_error_string_n(err, buf, sizeof(buf));fprintf(stderr, "OpenSSL error: %s\n", buf);fprintf(stderr, "  at %s:%d\n", file, line);if (data && (flags & ERR_TXT_STRING)) {fprintf(stderr, "  data: %s\n", data);}}
}// 在验证函数中使用示例
if (EVP_DigestVerifyInit(mdctx, NULL, EVP_sha256(), NULL, pubkey) != 1) {fprintf(stderr, "Error initializing verification\n");print_openssl_error();goto cleanup;
}

常见错误处理

  • 初始化失败 :检查公钥格式是否正确,算法是否支持。
  • 更新数据失败 :检查数据缓冲区是否有效。
  • 验证失败 :检查签名数据是否正确,公钥是否匹配私钥。
  • 内存错误 :确保所有分配的资源都被正确释放。

文件验签与字符串验签对比

特性文件验签字符串验签
数据来源文件系统内存中的字符串
数据加载需要文件 I/O 操作直接使用内存数据
大数据处理可分块读取处理通常一次性处理
适用场景验证文件完整性验证 API 消息、配置数据等
性能考虑受磁盘 I/O 影响纯内存操作,速度更快

总结与最佳实践

  • 算法选择 :推荐使用 SHA - 256 或更强的哈希算法,避免 MD5 或 SHA1 等不安全算法。
  • 密钥管理 :妥善保管私钥,公钥可通过证书链验证真实性。
  • 错误处理 :始终检查 OpenSSL 函数返回值,实现全面错误处理。
  • 资源管理 :确保分配资源正确释放,防止内存泄漏。
  • 性能优化 :大文件可分块调用 EVP_DigestVerifyUpdate,避免一次性加载。

相关文章:

  • 行为型:状态模式
  • AI时代新词-大模型(Large Language Model)
  • @MySQL升级8.0.42(Ubuntu 22.04)-SOP
  • 软考 系统架构设计师系列知识点之杂项集萃(75)
  • charAt()
  • 高性能排行榜系统架构实战
  • 白平衡校正中冯・克里兹参数计算过程详解
  • STM32之SPI——外部FLASH和RFID
  • 高阶数据结构——哈希表的实现
  • 数据结构第5章 树与二叉树(竟成)
  • 篇章三 数据结构——前置知识(三)
  • 1. 数据结构
  • 数据结构-图的应用,实现环形校验和拓扑排序
  • 【数据结构】哈希表的实现
  • 【数据结构】--二叉树--堆(上)
  • 如何用 SQL 找到最受欢迎的用户?
  • 数据结构第3章 线性表 (竟成)
  • Dify中的Extension插件开发例子:以neko为例
  • 二叉树遍历
  • RT-Thread源码阅读(3)——内核对象管理
  • 锦州公司做网站/手机版百度一下
  • wordpress侧栏/北京seo网络优化师
  • .net 手机网站开发/腾讯域名
  • 多语言网站如何做/百度推广代理怎么加盟
  • 做seo网站诊断书怎么做/seo全网图文推广
  • 大厂做网站shijuewang/发布平台有哪些