仿射密码是一种古典密码,它是通过对明文中的每个字符进行数学变换来加密消息的。这种加密方法最初被使用于古罗马时期,凯撒密码就是一种简单的仿射密码。在现代密码学中,仿射密码已经不再被视为安全的加密方法,因为它容易受到各种攻击,如频率分析攻击和线性密码分析攻击。然而,仿射密码仍然具有教育意义,可以帮助初学者了解密码学的基本概念和技术。
优点:
仿射密码简单易懂,容易实现和使用。
加密速度快,因为使用的是基本的算术运算。
支持多种语言,包括中文、英文等。
提供了一定程度的安全保障,可以用于一些低级别的加密需求。
缺点:
安全性较低,容易受到暴力破解和频率分析等攻击。
密钥空间较小,只有 2626种可能的密钥。
容易受到已知明文攻击。
不支持数字和标点符号等特殊字符的加密。
总体而言,由于仿射密码的安全性相对较弱,因此在现代加密技术中很少被使用。
一、实验目的
通过实现简单的古典密码算法,理解密码学的相关概念,如明文、密文、加 密密钥、解密密钥、加密算法、解密算法、流密码与分组密码等。
二、实验要求
(1)只有一个加密器,解密使用加密器实现;
(2)随机生成密钥;
(3)扩展欧几里得算法计算逆元;
(4)文件读写明密文;
三、实验原理
加密:
解密:
其中a, b为密钥,,且gcd(a, 26)=1
本程序还要求a≠0或1,当a=1时,仿射密码变为加法密码,当a=0时,仿射密码变为乘法密码。
四、实验过程:
1.参数随机选取生成密钥:
void key(int *a, int *b) {
srand(time(NULL));
//随机种子生成-->time得到每次程序运行的时间,每一次运行程序的时间是不同的
//实现一次一密
int x, y;
do {
*a = rand() % N;
} while (gcd(*a, N, &x, &y) != 1 || *a == 1 || *a == 0);
do {
*b = rand() % N;
} while (*b == 0);
printf("a=%d,b=%d\n", *a, *b);
}
2.欧几里得算法求最大公约数——验证正确性
若a与p互素,则满足( a × x ) m o d p = 1 (a\times x) mod p=1(a×x)modp=1的x为a的逆元。
int gcd(int a, int b, int *x, int *y) {
if (b == 0) {
*x = 1;
*y = 0;
return a;
}
int d = gcd(b, a % b, y, x);
*y -= a / b * (*x);
return d;
}
3.文本文件相关函数——读取写入文件:
需了解C语言文本文件读取相关函数:文本文件是指以ASCII码方式(也称文本方式)存储的文件,更确切地说,英文、数字等字符存储的是ASCII码,而汉字存储的是机内码。
引入头文件:#include<stdio.h>
打开文件fopen函数:返回指针类型
FILE *fopen(char *filename, *type);
常见type有:
"r" 打开一个用于读取的文件。该文件必须存在。
"w" 创建一个用于写入的空文件。如果文件名称与已存在的文件相同,则会删除已有文件的内容,文件被视为一个新的空文件。
"a" 追加到一个文件。写操作向文件末尾追加数据。如果文件不存在,则创建文件。
"r+"打开一个用于更新的文件,可读取也可写入。该文件必须存在。
"w+"创建一个用于读写的空文件。
"a+"打开一个用于读取和追加的文件。
读取文件 fread 函数:
fread( void *buffer, filesize, size_t count, FILE *stream );
关闭文件fclose函数:
fclose(fp);//fp是文本地址 FILE *fp
文件写入:最后一定要关闭文件,否则内容可能会丢失
fopen("text.txt","w");//覆盖式写入
int fputs( const char *str, FILE *stream );
指针定位fseek 函数:
int fseek( FILE *stream, long offset, int origin );
原始值 | 含义 |
---|---|
SEEK_CUR | 文件指针的当前位置 |
SEEK_END | 文件结尾 |
SEEK_SET | 文件开头 |
获取当前指针位置相对于文件首地址的偏移字节数ftell()函数:用于计算文件大小
long ftell(FILE *stream);
……(参考C语言 文本文件读取、写入与定位(详细介绍)_c语言写入文件_Gretel Tade的博客-CSDN博客)
char *readfile(const char *fname) {
FILE *file = fopen(fname, "r");//读取文件
if (file == NULL) {
return NULL;
}
fseek(file, 0, SEEK_END);//将指针移到文件末尾
long fsize = ftell(file);//获取当前偏移字节从而获得文件大小
fseek(file, 0, SEEK_SET);//将指针移回文件开头
char *s = (char *) malloc(fsize + 1);
if (s == NULL) {
fclose(file);
return NULL;
}
if (fread(s, fsize, 1, file) != 1) {//读取文件
free(s);
fclose(file);
return NULL;
}
s[fsize] = '\0';//加入读取终止标识符
fclose(file);
return s;
}
4.加密函数:
char *encrypt(int a, const char *fname, int b) {
char *s = readfile(fname);
printf("明文为:%s\n", s); // 打印文件内容
char *encrypted = (char *)malloc(strlen(s) + 1);//动态分配内存空间,使用完一定要释放
if (encrypted == NULL) {
free(s);
return NULL;
}
int len = strlen(s);
printf("Lenth:%d\n", len);
int i, z = 0;
//将大小写字母分别加密
for (i = 0; i < len; i++) {
if (s[i] >= 'A' && s[i] <= 'Z') {
s[z] = (a * (s[i] - 'A') + b) % 26 + 'A';
} else if (s[i] >= 'a' && s[i] <= 'z') {
s[z] = (a * (s[i] - 'a') + b) % 26 + 'a';
} else {
s[z] = s[i];//特殊字符原样输出
}
z++;
}
s[z] = '\0'; // 添加终止字符
//输出密文
printf("密文为:%s\n", s);
return s;
}
5.扩展欧几里得算法——求取逆元
int epgcd(int a) {
int x, y;
int d = gcd(a, N, &x, &y);
if (d != 1) {
return -1; // a 和 m 不互质,无法求逆元
}
return (x % N + N) % N;
}
(未解决)问题3:输出中文乱码。
(未解决)(1)改进将仿射密码定义在 Z29上,明密文空间除 26 个英文字母还包括空格、句号和 引号。这个方案与传统定义在 Z26上的仿射密码相比有何优点?如果要实现此方案,你的程 序应该如何进行调整?