news 2025/12/28 12:54:56

C语言实现仿射变换加解密算法(附带源码)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C语言实现仿射变换加解密算法(附带源码)

一、项目背景详细介绍

在现代密码学研究中,虽然我们主要使用对称加密算法(如 AES)、非对称加密算法(如 RSA、ECC)、哈希函数、认证协议等,但在密码学历史发展过程中,早期的加密方式(称为古典密码学)占据重要地位,例如:

  • 凯撒密码(Caesar Cipher)

  • 维吉尼亚密码(Vigenère Cipher)

  • 替换密码(Substitution Cipher)

  • 换位密码(Transposition Cipher)

而在替换密码族中非常重要的一类便是仿射密码(Affine Cipher),它是对凯撒密码的扩展,通过在模 26 下对字符进行一次线性变换来实现加密与解密。

仿射密码形式化表达为:

由于计算简单、结构明确,仿射密码是密码学课程中常见的教学案例,同时也常用于入门级密码算法实现训练。

本项目的目标是利用纯 C 语言实现一个完整可用、带加密与解密、支持大小写字母、保留非字母字符的仿射密码程序,并包含完整技术讲解、算法实现、测试程序与扩展方向讨论。


二、项目需求详细介绍

为了让程序具有工程级可用性,需要严格定义项目需求。以下为本项目的功能需求与工程约束。

1. 功能性需求

  1. 实现仿射加密(Encryption)

    • 输入明文字符串 P

    • 输入密钥参数 a、b

    • 输出密文字符串 C

  2. 实现仿射解密(Decryption)

    • 输入密文字符串 C

    • 输入相同的 a、b

    • 能正确恢复明文 P

  3. 支持大小写字母,保持大小写一致

    • 大写 A–Z 映射到 0–25

    • 小写 a–z 映射到 0–25

    • 输出保持大小写不变

  4. 非字母字符保持不变

    • 数字、标点、空格全部原样输出

  5. 检测参数合法性

    • a 必须与 26 互素,否则无逆元

    • b 可以取任何整数,内部会模 26 处理

  6. 包含命令行模式与交互模式

    • 可使用命令行:
      ./affine enc a b "text"

    • 无参数时进入交互式运行

  7. 具有完善的注释、教学性结构与可复用性


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
4gcd=2
6gcd=2
10gcd=2
13gcd=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 区间内。


四、实现思路详细介绍

实现步骤如下:

  1. 读取参数(a,b,字符串)

  2. 校验 a 是否与 26 互素

  3. 加密:

    • 判断字符种类(大写、小写、非字母)

    • 字母映射到数字 P

    • 应用公式 C = (aP + b) % 26

    • 映射回字母

  4. 解密:

    • 求 a 的模逆 a⁻¹

    • P = a⁻¹(C - b) % 26

  5. 输出转换后的字符串

  6. 支持命令行或交互模式

整个流程结构清晰、逻辑简单、易读易懂,非常适合作为 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 个合法值,很快)

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