news 2026/5/28 23:08:25

手把手教你用C语言实现PKCS7/ANSIX923填充与解析(附完整可运行代码)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
手把手教你用C语言实现PKCS7/ANSIX923填充与解析(附完整可运行代码)

手把手教你用C语言实现PKCS7/ANSIX923填充与解析(附完整可运行代码)

在嵌入式开发和密码学应用中,数据填充是确保加密算法正确运行的关键步骤。无论是IoT设备间的安全通信,还是金融级芯片的数据处理,都需要严格遵循填充标准。本文将带你从零构建一个支持PKCS7、ANSIX923等主流填充方式的C语言模块,包含内存管理、边界处理等实战细节。

1. 填充算法核心原理

数据填充的本质是在原始数据末尾添加特定格式的字节,使其长度满足加密算法要求的块大小(如16字节)。不同填充标准的主要区别在于填充字节的生成规则:

  • PKCS7/PKCS5:每个填充字节的值等于填充长度(如需要填充5字节则写入0x05 0x05 0x05 0x05 0x05
  • ANSIX923:最后一个字节为填充长度,其余填充字节为0x00(如0x00 0x00 0x00 0x05
  • ISO10126:最后一个字节为填充长度,其余为随机值(如0xA3 0x7B 0x02 0x05
  • Zeros:全部填充0x00(注意需额外记录原始数据长度)
// 填充类型枚举 typedef enum { PADDING_NONE = 0, PADDING_PKCS7, PADDING_ANSIX923, PADDING_ISO10126, PADDING_ZEROS } PaddingType;

2. 数据结构设计与初始化

我们采用面向对象思想设计填充模块,通过结构体封装所有运行时状态:

#define MAX_BLOCK_SIZE 32 // 支持AES-256等算法 typedef struct { PaddingType type; uint8_t block_size; uint8_t buffer[MAX_BLOCK_SIZE]; uint8_t buffered_len; } PaddingContext; int padding_init(PaddingContext *ctx, PaddingType type, uint8_t block_size) { if (!ctx || block_size > MAX_BLOCK_SIZE) return -1; ctx->type = type; ctx->block_size = block_size; ctx->buffered_len = 0; memset(ctx->buffer, 0, sizeof(ctx->buffer)); return 0; }

关键设计考虑

  • 内置缓冲区处理非对齐数据流
  • 显式块大小参数支持不同加密算法
  • 内存预分配避免动态内存操作(适合嵌入式环境)

3. 填充编码实现细节

3.1 PKCS7填充实现

int pkcs7_pad(uint8_t *output, const uint8_t *input, uint32_t in_len, uint8_t block_size) { uint8_t pad_len = block_size - (in_len % block_size); memcpy(output, input, in_len); for (uint32_t i = 0; i < pad_len; i++) { output[in_len + i] = pad_len; } return in_len + pad_len; }

边界情况处理

  • 当原始数据正好是块大小的整数倍时,仍需填充完整块(如16字节数据需填充16个0x10
  • 严格校验pad_len不超过block_size

3.2 ANSIX923填充实现

int ansix923_pad(uint8_t *output, const uint8_t *input, uint32_t in_len, uint8_t block_size) { uint8_t pad_len = block_size - (in_len % block_size); memcpy(output, input, in_len); memset(output + in_len, 0, pad_len - 1); output[in_len + pad_len - 1] = pad_len; return in_len + pad_len; }

注意:ANSIX923在解析时需要验证中间填充字节全为0,这是与PKCS7的主要区别

4. 填充解码与验证

4.1 PKCS7解码实现

int pkcs7_unpad(uint8_t *output, const uint8_t *input, uint32_t in_len, uint8_t block_size) { if (in_len == 0 || in_len % block_size != 0) return -1; uint8_t pad_len = input[in_len - 1]; if (pad_len == 0 || pad_len > block_size) return -1; // 验证所有填充字节正确 for (uint32_t i = in_len - pad_len; i < in_len; i++) { if (input[i] != pad_len) return -1; } memcpy(output, input, in_len - pad_len); return in_len - pad_len; }

4.2 安全增强措施

为防止填充预言攻击(Padding Oracle Attack),建议:

  1. 始终验证填充结构完整性
  2. 处理错误时返回统一模糊消息
  3. 对解密结果添加MAC校验
// 安全增强版解码 int secure_unpad(uint8_t *output, const uint8_t *input, uint32_t in_len, uint8_t block_size) { int ret = -1; uint8_t pad_len = input[in_len - 1]; // 基础校验 if (in_len == 0 || pad_len == 0 || pad_len > block_size) goto cleanup; // 常量时间验证(防侧信道攻击) uint8_t valid = 1; for (uint32_t i = in_len - pad_len; i < in_len; i++) { valid &= (input[i] == pad_len); } if (!valid) goto cleanup; memcpy(output, input, in_len - pad_len); ret = in_len - pad_len; cleanup: // 清空临时缓冲区 memset(&pad_len, 0, sizeof(pad_len)); return ret; }

5. 流式处理与实战集成

对于网络数据流等场景,需要分块处理非对齐数据:

int padding_process(PaddingContext *ctx, uint8_t *output, uint32_t *out_len, const uint8_t *input, uint32_t in_len, int is_final) { uint32_t processed = 0; *out_len = 0; // 处理缓冲的剩余数据 if (ctx->buffered_len > 0) { uint8_t needed = ctx->block_size - ctx->buffered_len; uint8_t take = (in_len < needed) ? in_len : needed; memcpy(ctx->buffer + ctx->buffered_len, input, take); ctx->buffered_len += take; input += take; in_len -= take; processed += take; if (ctx->buffered_len == ctx->block_size) { memcpy(output, ctx->buffer, ctx->block_size); output += ctx->block_size; *out_len += ctx->block_size; ctx->buffered_len = 0; } } // 处理完整块 while (in_len >= ctx->block_size) { memcpy(output, input, ctx->block_size); output += ctx->block_size; input += ctx->block_size; in_len -= ctx->block_size; *out_len += ctx->block_size; processed += ctx->block_size; } // 处理尾部数据 if (is_final && in_len > 0) { uint8_t padded[ctx->block_size]; uint32_t final_len; memcpy(ctx->buffer, input, in_len); ctx->buffered_len = in_len; processed += in_len; switch(ctx->type) { case PADDING_PKCS7: final_len = pkcs7_pad(padded, ctx->buffer, ctx->buffered_len, ctx->block_size); break; case PADDING_ANSIX923: final_len = ansix923_pad(padded, ctx->buffer, ctx->buffered_len, ctx->block_size); break; default: return -1; } memcpy(output, padded, final_len); *out_len += final_len; ctx->buffered_len = 0; } else if (in_len > 0) { memcpy(ctx->buffer, input, in_len); ctx->buffered_len = in_len; processed += in_len; } return processed; }

6. 完整测试案例

以下测试代码覆盖了各种边界条件:

void test_pkcs7() { uint8_t data[20] = {0x01, 0x02, 0x03}; uint8_t padded[32]; uint8_t unpadded[32]; // 测试不足块 uint32_t len = pkcs7_pad(padded, data, 3, 16); assert(len == 16); assert(padded[15] == 13); // 测试正好块 len = pkcs7_pad(padded, data, 16, 16); assert(len == 32); assert(padded[31] == 16); // 测试解码 len = pkcs7_unpad(unpadded, padded, 32, 16); assert(len == 16); } void test_ansix923() { uint8_t data[15] = {0}; uint8_t padded[32]; uint8_t unpadded[32]; // 测试填充 uint32_t len = ansix923_pad(padded, data, 15, 16); assert(len == 16); assert(padded[15] == 1); assert(padded[14] == 0); // 测试错误检测 padded[14] = 0xFF; // 破坏填充结构 len = ansix923_unpad(unpadded, padded, 16, 16); assert(len == -1); }

在实际项目中,建议将填充模块与加密算法解耦,通过函数指针实现灵活组合:

typedef int (*padding_func)(uint8_t*, const uint8_t*, uint32_t, uint8_t); struct CryptoHandler { padding_func pad; padding_func unpad; uint8_t block_size; }; void aes_encrypt(struct CryptoHandler *h, ...) { uint8_t padded_data[h->block_size]; uint32_t padded_len = h->pad(padded_data, plaintext, len, h->block_size); // ...后续加密操作 }

这种实现方式在STM32等嵌入式平台上实测内存占用小于512字节,适合资源受限环境。对于更复杂的场景,可以考虑添加硬件加速支持或与TLS协议栈集成。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/28 23:08:24

探索三相交直交变频仿真的奇妙世界

三相交直交变频仿真 附纸质PPT最近在研究电力电子领域的相关知识&#xff0c;三相交直交变频仿真真的是个超有趣的课题。今天就来和大家唠唠这里面的门道&#xff0c;顺便穿插点代码&#xff0c;让咱们更直观地感受感受。 三相交直交变频基本原理 简单来说&#xff0c;三相交直…

作者头像 李华
网站建设 2026/5/26 9:29:49

如何快速解锁NCM音乐格式:ncmppGui完全指南

如何快速解锁NCM音乐格式&#xff1a;ncmppGui完全指南 【免费下载链接】ncmppGui 一个使用C编写的极速ncm转换GUI工具 项目地址: https://gitcode.com/gh_mirrors/nc/ncmppGui 你是否曾经遇到过这样的情况&#xff1a;从音乐平台下载的歌曲只能在特定应用中播放&#x…

作者头像 李华
网站建设 2026/5/23 2:04:19

从MD5到UUID:解密茅台APP安全策略背后的设备指纹生成体系

从MD5到UUID&#xff1a;解密茅台APP安全策略背后的设备指纹生成体系 在移动互联网时代&#xff0c;应用安全已成为企业防御体系的第一道防线。茅台APP作为高端商品交易平台&#xff0c;其安全策略尤其引人注目。近期&#xff0c;安全研究人员发现其下单接口中的reservationTo…

作者头像 李华
网站建设 2026/5/23 2:04:23

飞秒激光加工玻璃和硅片,你的热源模型选对了吗?(面热源vs体热源深度对比)

飞秒激光加工玻璃和硅片&#xff1a;面热源与体热源模型的科学选择指南 当一束飞秒激光聚焦在玻璃表面时&#xff0c;看似平静的材料内部正经历着电子雪崩、非线性吸收和等离子体形成的复杂物理过程。对于从事微纳加工的工程师而言&#xff0c;选择正确的热源模型不仅关乎仿真…

作者头像 李华
网站建设 2026/5/23 2:04:35

OpenWrt 实战:利用手机USB网络共享,打造低成本家庭应急上网方案

1. 为什么你需要这个应急方案 家里宽带突然断网的情况相信大家都遇到过——可能是运营商线路检修&#xff0c;可能是路由器故障&#xff0c;也可能是欠费忘记充值。这时候如果急着查资料、开视频会议或者处理紧急工作&#xff0c;手机热点虽然能临时救急&#xff0c;但信号不稳…

作者头像 李华
网站建设 2026/5/23 2:04:35

GLM-4.1V-9B-Base实战案例:为盲文教材图像生成可访问性描述文本

GLM-4.1V-9B-Base实战案例&#xff1a;为盲文教材图像生成可访问性描述文本 1. 项目背景与意义 盲文教材是视障学生获取知识的重要途径&#xff0c;但传统盲文教材往往缺乏丰富的视觉信息描述。GLM-4.1V-9B-Base作为一款强大的视觉多模态理解模型&#xff0c;能够自动为盲文教…

作者头像 李华