一、项目背景详细介绍
在现代密码学研究中,虽然我们主要使用对称加密算法(如 AES)、非对称加密算法(如 RSA、ECC)、哈希函数、认证协议等,但在密码学历史发展过程中,早期的加密方式(称为古典密码学)占据重要地位,例如:
凯撒密码(Caesar Cipher)
维吉尼亚密码(Vigenère Cipher)
替换密码(Substitution Cipher)
换位密码(Transposition Cipher)
而在替换密码族中非常重要的一类便是仿射密码(Affine Cipher),它是对凯撒密码的扩展,通过在模 26 下对字符进行一次线性变换来实现加密与解密。
仿射密码形式化表达为:
由于计算简单、结构明确,仿射密码是密码学课程中常见的教学案例,同时也常用于入门级密码算法实现训练。
本项目的目标是利用纯 C 语言实现一个完整可用、带加密与解密、支持大小写字母、保留非字母字符的仿射密码程序,并包含完整技术讲解、算法实现、测试程序与扩展方向讨论。
二、项目需求详细介绍
为了让程序具有工程级可用性,需要严格定义项目需求。以下为本项目的功能需求与工程约束。
1. 功能性需求
实现仿射加密(Encryption)
输入明文字符串 P
输入密钥参数 a、b
输出密文字符串 C
实现仿射解密(Decryption)
输入密文字符串 C
输入相同的 a、b
能正确恢复明文 P
支持大小写字母,保持大小写一致
大写 A–Z 映射到 0–25
小写 a–z 映射到 0–25
输出保持大小写不变
非字母字符保持不变
数字、标点、空格全部原样输出
检测参数合法性
a 必须与 26 互素,否则无逆元
b 可以取任何整数,内部会模 26 处理
包含命令行模式与交互模式
可使用命令行:
./affine enc a b "text"无参数时进入交互式运行
具有完善的注释、教学性结构与可复用性
3. 设计目标
简洁性:不依赖任何第三方库
可读性:所有算法清晰可理解
正确性:能正确加解密所有包含字母的文本
可扩展性:后续易扩展到 Unicode 或任意字符表
三、相关技术详细介绍
为了高质量实现仿射密码,需要掌握以下几项基础技术:ASCII 编码、模运算、模逆求解、字符分类等。
以下对相关知识进行系统讲解。
1. 字母映射与 ASCII 编码
在 ASCII 中:
| 字符 | ASCII | 范围映射 |
|---|---|---|
| 'A'–'Z' | 65–90 | 映射到 0–25 |
| 'a'–'z' | 97–122 | 映射到 0–25 |
映射方式为:
大写:
P = ch - 'A'小写:
P = ch - 'a'
同理从数字映射回字母:
大写:
ch = 'A' + P小写:
ch = 'a' + P
这个方法是所有古典密码实现的核心基础。
2. 仿射加密公式
其中:
a为乘法变换系数b为平移偏移量
对每个字母的数字编号都有如下线性变换。
可用的 a 值为:
| a | 是否可逆 |
|---|---|
| 1 | √ |
| 3 | √ |
| 5 | √ |
| 7 | √ |
| 9 | √ |
| 11 | √ |
| 15 | √ |
| 17 | √ |
| 19 | √ |
| 21 | √ |
| 23 | √ |
| 25 | √ |
不能用:
| a | 原因 |
|---|---|
| 2 | 与 26 公因数为 2 |
| 4 | gcd=2 |
| 6 | gcd=2 |
| 10 | gcd=2 |
| 13 | gcd=13 |
本项目使用扩展欧几里得算法(Extended Euclidean Algorithm)求逆元 a⁻¹。
4. 扩展欧几里得算法(Extended GCD)
扩展欧几里得算法可以求解:
ax+by=gcd(a,b)ax + by = gcd(a, b)ax+by=gcd(a,b)
如果 gcd(a,26)=1,则 x 即为 a 的模逆(做一次取模)。
我们将其封装为函数mod_inverse(a,26,&inv)。
5. 字符处理:保持大小写与非字母字符
本项目中:
A~Z 使用独立的 0~25 映射
a~z 使用独立的 0~25 映射
非字母不变
这是为了保持原文结构与可读性。
6. 模运算正数化处理
C 语言的%会产生负值,因此需要手写:
int mod_pos(int x, int m) { int r = x % m; return (r < 0) ? r + m : r; }
这保证了所有 mod 运算落在 0~25 区间内。
四、实现思路详细介绍
实现步骤如下:
读取参数(a,b,字符串)
校验 a 是否与 26 互素
加密:
判断字符种类(大写、小写、非字母)
字母映射到数字 P
应用公式 C = (aP + b) % 26
映射回字母
解密:
求 a 的模逆 a⁻¹
P = a⁻¹(C - b) % 26
输出转换后的字符串
支持命令行或交互模式
整个流程结构清晰、逻辑简单、易读易懂,非常适合作为 C 语言工程初学者实现练习。
五、完整实现代码
/******************************************************************** * 文件:affine_cipher.c * 功能:实现仿射密码(Affine Cipher)的加密与解密(大小写保持) * 作者:ChatGPT (用于教学与博客文章) ********************************************************************/ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> /******************************************************************** * 函数名称:mod_pos * 作用:计算 x mod m 的正值代表,将负数结果转换成 0~m-1 的合法范围 ********************************************************************/ static int mod_pos(int x, int m) { int r = x % m; return (r < 0) ? r + m : r; } /******************************************************************** * 扩展欧几里得算法:用来求 ax + by = gcd(a, b) ********************************************************************/ static int extended_gcd(int a, int b, int* x, int* y) { if (b == 0) { if (x) *x = (a >= 0 ? 1 : -1); if (y) *y = 0; return abs(a); } int x1, y1; int g = extended_gcd(b, a % b, &x1, &y1); if (x) *x = y1; if (y) *y = x1 - (a / b) * y1; return g; } /******************************************************************** * 函数名称:mod_inverse * 作用:求 a 在模 m 下的逆元,若有逆元写入 *inv,返回 1,否则返回 0 ********************************************************************/ static int mod_inverse(int a, int m, int* inv) { int x, y; int g = extended_gcd(a, m, &x, &y); if (g != 1) return 0; // 不互素,无逆元 if (inv) *inv = mod_pos(x, m); return 1; } /******************************************************************** * 函数名称:valid_a * 作用:判断 a 是否与 26 互素 ********************************************************************/ static int valid_a(int a) { int x, y; return extended_gcd(a, 26, &x, &y) == 1; } /******************************************************************** * 添加仿射加密变换(单字符) ********************************************************************/ static char affine_encrypt_char(char ch, int a, int b) { if (isupper((unsigned char)ch)) { int p = ch - 'A'; int c = mod_pos(a * p + b, 26); return 'A' + c; } else if (islower((unsigned char)ch)) { int p = ch - 'a'; int c = mod_pos(a * p + b, 26); return 'a' + c; } return ch; // 非字母不变 } /******************************************************************** * 添加仿射解密变换(单字符) ********************************************************************/ static char affine_decrypt_char(char ch, int a_inv, int b) { if (isupper((unsigned char)ch)) { int c = ch - 'A'; int p = mod_pos(a_inv * (c - b), 26); return 'A' + p; } else if (islower((unsigned char)ch)) { int c = ch - 'a'; int p = mod_pos(a_inv * (c - b), 26); return 'a' + p; } return ch; } /******************************************************************** * 仿射加密整个字符串,返回新字符串(需 free) ********************************************************************/ static char* affine_encrypt_str(const char* in, int a, int b) { size_t n = strlen(in); char* out = (char*)malloc(n + 1); if (!out) return NULL; for (size_t i = 0; i < n; i++) out[i] = affine_encrypt_char(in[i], a, b); out[n] = '\0'; return out; } /******************************************************************** * 仿射解密整个字符串,返回新字符串(需 free) ********************************************************************/ static char* affine_decrypt_str(const char* in, int a, int b) { int a_inv; if (!mod_inverse(a, 26, &a_inv)) return NULL; // 无逆元无法解密 size_t n = strlen(in); char* out = (char*)malloc(n + 1); if (!out) return NULL; for (size_t i = 0; i < n; i++) out[i] = affine_decrypt_char(in[i], a_inv, b); out[n] = '\0'; return out; } /******************************************************************** * main 部分:支持命令行模式和交互模式 ********************************************************************/ int main(int argc, char* argv[]) { if (argc == 5) { const char* op = argv[1]; int a = atoi(argv[2]); int b = atoi(argv[3]); const char* text = argv[4]; if (!valid_a(a)) { fprintf(stderr, "错误:a=%d 与 26 不互素,无法加解密。\n", a); return 1; } if (strcmp(op, "enc") == 0) { char* out = affine_encrypt_str(text, a, b); printf("%s\n", out); free(out); } else if (strcmp(op, "dec") == 0) { char* out = affine_decrypt_str(text, a, b); printf("%s\n", out); free(out); } else { printf("用法: ./affine enc a b \"text\"\n"); } return 0; } printf("进入交互模式:\n"); char mode[8], buf[1024]; int a, b; printf("输入模式 (enc/dec):"); scanf("%7s", mode); printf("输入 a 和 b(a 与 26 互素):"); scanf("%d %d", &a, &b); getchar(); // 清除换行 printf("输入文本:"); fgets(buf, sizeof(buf), stdin); buf[strcspn(buf, "\n")] = 0; if (strcmp(mode, "enc") == 0) { char* out = affine_encrypt_str(buf, a, b); printf("密文:%s\n", out); free(out); } else { char* out = affine_decrypt_str(buf, a, b); printf("明文:%s\n", out); free(out); } return 0; }六、代码详细解读
1.mod_pos()
用于修复 C 语言%运算产生负数的问题,确保所有模运算结果在 0~25 之间。
2.extended_gcd()
通过扩展欧几里得算法求解a*x + b*y = gcd(a,b)
用于计算模逆。
3.mod_inverse()
用扩展 GCD 求 a 在模 26 下的逆元,没有逆元返回失败。
4.valid_a()
判断 a 与 26 的最大公约数是否为 1,用于参数校验。
5.affine_encrypt_char()和affine_decrypt_char()
仿射密码的核心。
负责字母映射、加解密运算、大小写保持。
6.affine_encrypt_str()/affine_decrypt_str()
对整个字符串进行加密或解密操作。
7.main()
支持:
命令行模式
交互模式
完整工程级交互方式。
七、项目详细总结
本项目通过 C 语言实现了古典密码学中非常重要的仿射密码(Affine Cipher),既包含算法基础知识,也包含数学推导、逆元求解、字符处理与工程实现。整个项目从需求、技术、实现到扩展完整呈现,为学习密码学算法实现、学习 C 语言字符操作、数学在编程中的应用提供了一个优秀的案例。
特别是对 a 的模逆处理,是理解模算术与加密算法的关键一环。本项目使用扩展欧几里得算法,使得算法具有普适性与数学严谨性。
八、项目常见问题及解答
1. a 为何必须与 26 互素?
因为需要求逆元。没有逆元就无法解密。
2. 可以把所有字母统一变成大写再加密吗?
可以。
这会让人更容易观察算法过程。
3. 为什么非字母字符不变?
为了增强可读性,也是实际工程中常见需求。
4. b 是否必须小于 26?
不必须。
程序内部会取模(b % 26)。
5. 模逆一定存在吗?
只有 a 与 26 互素时才存在。
九、扩展方向与性能优化
1. 扩展为支持全部 ASCII 字符
把模从 m=26 扩展到 m=95(可打印字符),更实用。
2. 支持 Unicode(如中文)
需要使用更大的模数,并自定义字符表。
3. 将程序封装成库函数(库文件 + 头文件)
适合在更大的项目中复用。
4. 增加自动破解功能(攻击)
如:
频率分析(适用于 affine cipher)
穷举 a,b(当 a 值只有 12 个合法值,很快)