news 2026/6/22 19:04:05

手把手教你用纯C语言(仅stdio.h)实现SM4国密算法,附完整可运行代码

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
手把手教你用纯C语言(仅stdio.h)实现SM4国密算法,附完整可运行代码

从零实现SM4国密算法:仅用stdio.h的C语言实战指南

1. 密码学初学者的SM4实现困境

许多刚接触密码学的开发者都会遇到一个典型困境:算法原理看似清晰,但实际编码时却无从下手。SM4作为我国商用密码标准算法,其官方文档充斥着数学符号和理论描述,对于C语言初学者而言,要实现一个完整可用的SM4加密程序更是难上加难。

常见的问题包括:

  • 如何在不依赖外部库的情况下处理位运算?
  • 128位数据块在C语言中如何表示和操作?
  • 轮密钥生成的复杂变换如何用基本语法实现?

核心挑战在于:大多数教程要么过于理论化,要么依赖第三方库,缺少从第一性原理出发的纯C实现指导。这正是本文要解决的关键问题——我们将仅使用C标准库中的stdio.h,构建完整的SM4加密解密系统。

2. SM4算法核心组件拆解

2.1 基础运算单元实现

SM4算法的基石是两个基本运算:模2加(异或)和循环移位。在纯C环境中,我们需要自己实现这些基础操作:

// 32位循环左移实现 unsigned int rotate_left(unsigned int value, int shift) { return (value << shift) | (value >> (32 - shift)); } // 模2加就是C语言的异或运算 unsigned int xor_operation(unsigned int a, unsigned int b) { return a ^ b; }

2.2 S盒的非线性变换

SM4的S盒是一个256字节的置换表,实现字节级的非线性替换。我们需要将其定义为静态常量:

static const unsigned char SBOX[256] = { 0xd6,0x90,0xe9,0xfe,0xcc,0xe1,0x3d,0xb7,0x16,0xb6,0x14,0xc2, // ...完整的S盒数据 0xcb,0x39,0x48 }; unsigned int sbox_transform(unsigned int word) { unsigned char bytes[4]; bytes[0] = (word >> 24) & 0xFF; bytes[1] = (word >> 16) & 0xFF; bytes[2] = (word >> 8) & 0xFF; bytes[3] = word & 0xFF; return (SBOX[bytes[0]] << 24) | (SBOX[bytes[1]] << 16) | (SBOX[bytes[2]] << 8) | SBOX[bytes[3]]; }

3. 关键变换函数的实现

3.1 合成变换T的实现

合成变换T是SM4的核心,结合了非线性变换τ和线性变换L:

unsigned int linear_transform_L(unsigned int word) { return word ^ rotate_left(word, 2) ^ rotate_left(word, 10) ^ rotate_left(word, 18) ^ rotate_left(word, 24); } unsigned int transform_T(unsigned int word, int is_encrypt) { unsigned int tau = sbox_transform(word); return is_encrypt ? linear_transform_L(tau) : (tau ^ rotate_left(tau, 13) ^ rotate_left(tau, 23)); }

3.2 轮函数的结构

SM4的轮函数采用Feistel结构,32轮迭代处理:

void round_function(unsigned int block[4], unsigned int round_key, int round_index) { unsigned int temp = block[1] ^ block[2] ^ block[3] ^ round_key; temp = transform_T(temp, 1); block[4 % 4] = block[0] ^ temp; // 轮次完成后整体左移 for(int i = 0; i < 3; i++) { block[i] = block[i+1]; } block[3] = block[4 % 4]; }

4. 密钥扩展系统的实现

4.1 轮密钥生成算法

SM4的密钥扩展需要两个阶段处理:

// 密钥扩展常数 static const unsigned int FK[4] = { 0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc }; void key_expansion(unsigned int master_key[4], unsigned int round_keys[32]) { unsigned int K[36]; // 第一阶段:与FK异或 for(int i = 0; i < 4; i++) { K[i] = master_key[i] ^ FK[i]; } // 第二阶段:迭代生成轮密钥 for(int i = 0; i < 32; i++) { unsigned int temp = K[i+1] ^ K[i+2] ^ K[i+3] ^ CK[i]; K[i+4] = K[i] ^ transform_T(temp, 0); round_keys[i] = K[i+4]; } }

4.2 固定参数CK的定义

CK是密钥扩展中使用的32个固定参数:

static const unsigned int CK[32] = { 0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269, // ...完整CK参数 0x484f565d, 0x646b7279 };

5. 完整加密解密流程实现

5.1 加密函数实现

void sm4_encrypt(unsigned int plaintext[4], unsigned int round_keys[32], unsigned int ciphertext[4]) { unsigned int block[4]; memcpy(block, plaintext, sizeof(block)); // 32轮迭代 for(int i = 0; i < 32; i++) { round_function(block, round_keys[i], i); } // 最终反序处理 ciphertext[0] = block[3]; ciphertext[1] = block[2]; ciphertext[2] = block[1]; ciphertext[3] = block[0]; }

5.2 解密函数实现

SM4的解密与加密流程相同,只是轮密钥使用顺序相反:

void sm4_decrypt(unsigned int ciphertext[4], unsigned int round_keys[32], unsigned int plaintext[4]) { unsigned int reversed_keys[32]; for(int i = 0; i < 32; i++) { reversed_keys[i] = round_keys[31 - i]; } sm4_encrypt(ciphertext, reversed_keys, plaintext); }

6. 实战测试与验证

6.1 测试案例实现

使用标准测试向量验证实现正确性:

void test_vectors() { unsigned int plaintext[4] = { 0x01234567, 0x89abcdef, 0xfedcba98, 0x76543210 }; unsigned int key[4] = { 0x01234567, 0x89abcdef, 0xfedcba98, 0x76543210 }; unsigned int round_keys[32]; key_expansion(key, round_keys); unsigned int ciphertext[4]; sm4_encrypt(plaintext, round_keys, ciphertext); printf("加密结果:\n"); printf("%08x %08x %08x %08x\n", ciphertext[0], ciphertext[1], ciphertext[2], ciphertext[3]); unsigned int decrypted[4]; sm4_decrypt(ciphertext, round_keys, decrypted); printf("解密结果:\n"); printf("%08x %08x %08x %08x\n", decrypted[0], decrypted[1], decrypted[2], decrypted[3]); }

6.2 用户交互接口

实现基本的命令行交互:

int main() { unsigned int input[4], key[4], round_keys[32], output[4]; char choice; printf("SM4加密演示 (仅使用stdio.h)\n"); printf("选择模式 (e:加密 d:解密 t:测试): "); scanf("%c", &choice); if(choice == 't') { test_vectors(); return 0; } printf("输入128位数据(4个32位十六进制数):\n"); scanf("%8x%8x%8x%8x", &input[0], &input[1], &input[2], &input[3]); printf("输入128位密钥(4个32位十六进制数):\n"); scanf("%8x%8x%8x%8x", &key[0], &key[1], &key[2], &key[3]); key_expansion(key, round_keys); if(choice == 'e') { sm4_encrypt(input, round_keys, output); printf("密文: "); } else { sm4_decrypt(input, round_keys, output); printf("明文: "); } printf("%08x %08x %08x %08x\n", output[0], output[1], output[2], output[3]); return 0; }

7. 性能优化与安全考量

7.1 关键操作优化技巧

虽然我们的实现追求代码简洁,但仍有优化空间:

// 更高效的循环左移实现 #define ROTL32(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) // 查表优化的S盒实现 static inline unsigned int optimized_sbox(unsigned int word) { return (SBOX[(word >> 24) & 0xFF] << 24) | (SBOX[(word >> 16) & 0xFF] << 16) | (SBOX[(word >> 8) & 0xFF] << 8) | SBOX[word & 0xFF]; }

7.2 安全实现注意事项

即使在学习实现中,也应考虑:

  • 敏感数据(如密钥)使用后应及时清除
  • 避免在栈上分配大块内存
  • 检查输入数据的有效性
  • 防止缓冲区溢出
void secure_erase(void *ptr, size_t size) { volatile unsigned char *p = (volatile unsigned char *)ptr; while(size--) *p++ = 0; }

8. 扩展功能实现

8.1 文件加密工具

基于核心算法实现文件加密功能:

void encrypt_file(FILE *in, FILE *out, unsigned int key[4]) { unsigned int block[4], round_keys[32]; key_expansion(key, round_keys); while(fread(block, sizeof(unsigned int), 4, in) == 4) { sm4_encrypt(block, round_keys, block); fwrite(block, sizeof(unsigned int), 4, out); // 清除内存中的明文数据 secure_erase(block, sizeof(block)); } }

8.2 多模式支持

实现ECB、CBC等不同加密模式:

void cbc_encrypt(FILE *in, FILE *out, unsigned int key[4], unsigned int iv[4]) { unsigned int block[4], prev[4], round_keys[32]; memcpy(prev, iv, sizeof(prev)); key_expansion(key, round_keys); while(fread(block, sizeof(unsigned int), 4, in) == 4) { // CBC模式:先与前一密文块异或 for(int i = 0; i < 4; i++) { block[i] ^= prev[i]; } sm4_encrypt(block, round_keys, block); fwrite(block, sizeof(unsigned int), 4, out); memcpy(prev, block, sizeof(prev)); } }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/8 23:39:18

新手也能看懂的BUUCTF SQL注入实战:从热点链接挖出后台数据库

新手也能看懂的BUUCTF SQL注入实战&#xff1a;从热点链接挖出后台数据库在CTF竞赛中&#xff0c;SQL注入一直是Web安全领域的经典题型。但很多新手往往只盯着登录框反复尝试&#xff0c;却忽略了页面上那些看似无关的"热点新闻"、"最新动态"等链接。本文将…

作者头像 李华
网站建设 2026/6/8 23:31:02

SQL语言:日期函数

函数 作用 SYSDATE 1. 作用:返回数据库服务器当前的系统日期和时间。 2. 语法:SYSDATE 3. 默认情况下格式掩码为:DD-MON-RR。 4. SYSDATE不会返回本地的系统日期和时间。 MONTHS_BETWEEN 1. 作用:返回表示两个日期之间月数的数值。 2. 语法:MONTHS\_BETWEEN(end date, sta…

作者头像 李华
网站建设 2026/6/8 23:31:00

SQL语言:转换函数

函数转换可以分为两种:隐式数据类型转换和显式数据类型转换 隐式数据类型转换(Oracle根据需要自动转换) N2C/D2C:字符字段非常灵活,几乎允许存储所有类型的信息。因此,可以很方便地针DATA和NUMBER值转换为它们的字符形式。即实现数字到字符(Number to Character)和日期到字…

作者头像 李华