安全课-1.古典Caesar加密

前言

这几天上计算机安全课,讲了点密码学的东西,这次的代码是在Ubuntu上用C++进行编写的。
这次是非常简单的古典加密中的Caesar加密方法,下次应该会写对称加密DES和非对称加密RSA算法。


Caesar加密介绍

这个加密算法非常简单,方法就是把一个字符串中每一个从’a’-‘z’的字符向左或向右移动K个单位,如果大于z则从a开始。

eg.使用向右移动3位的方法
GNU Aspell is a Free and Open Source spell checker.
–>JQX Dvshoo lv d Iuhh dqg Rshq Vrxufh vshoo fkhfnhu.


具体要求

实现两个程序,分别进行加密和破解。

  1. 加密程序,用户给一个英文文本,选择偏移进行加密,得到加密后的文本。
  2. 解密程序,从可能的情况中进行发分析,判断解密出来的文本是否正确。

使用aspell检查单词拼写

因为在进行解密的时候需要判断解密出来的文本是否正确,要进行单词的拼写检查。
本来查了一下stackoverflow的一个有关问题,找到了这个enchant的github网址,但是我看了一下这个好像是底层库,并没有啥地方有这个库的使用说明(官网也没有,逃)大佬可以指点一下。

最后使用了aspell这个工具,这样的工具挺多的,如xxspell一堆。最后使用了aspell这个工具。非常简单,具体可以参照这个arch上aspell的wiki
首先安装aspell,然后创建一个文件并写入测试字符。

1
2
3
$ sudo apt-get install apsell
$ echo "apple woold">>1.txt
$ aspell check 1.txt

在执行之后我有错误提示

错误:No word lists can be found for the language “zh_CN”.

这是因为我的Ubuntu系统语言是中文,所以修改配置文件

1
2
3
4
$ sudo gedit /etc/aspell.conf
//在配置文件中添加一行
lang en
//保存退出

再次使用aspell check 1.txt就可以看到系统提示的拼写提示了。
我这儿的需求是测试有几个单词的拼写不正确,使用方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
$ echo "Iss thisss woold apple. That" > 1.txt
$ cat 1.txt
//output
Iss thisss woold apple. That
$ cat 1.txt | aspell list
//output输出字典查不到的单词
Iss
thisss
woold
$ cat 1.txt | aspell list | wc -l
//output使用管道统计字符的数量
3

基本使用就是这样,这个为后面的解密打下基础


加密程序

没啥好说,直接上代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <iostream>
#include <cstring>
using namespace std;

int main(int argc,char **argv)
{
if(argc!=3){
printf("usage: ./caesar_encryption [filename] [offset]\n");
printf("\t ./caesar_encryption in.txt 3");
exit(0);
}
// cout<<argc<<" "<<argv[1]<<" "<<argv[2]<<endl;

//使用Caeser的方式进行加密
freopen(argv[1],"r",stdin);
freopen("out.txt","w",stdout);

int offset = atoi(argv[2]);
char str[1025];
while (cin.getline(str,sizeof(str))) {
int len=strlen(str);
for(int i=0;i<len;i++){
if(str[i]>='a'&&str[i]<='z'){
if((str[i]+offset)>'z')
cout<<(char)(str[i]+offset-26);
else
cout<<(char)(str[i]+offset);
continue;
}
if((str[i]>='A'&&str[i]<='Z')){
if((str[i]+offset)>'Z')
cout<<(char)(str[i]+offset-26);
else
cout<<(char)(str[i]+offset);
continue;
}
cout<<str[i];
}
cout<<endl;
}

//告知用户已经加密完成
freopen("/dev/tty","w",stdout); //win重定向到CON,Linux重定向到/dev/tty
cout<<"已完成加密"<<endl;
}

Caesar加密


解密

也直接上代码吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#include <iostream>
#include <cstring>
using namespace std;

void executeCMD(const char *cmd, char *result)
{
char buf_ps[1024];
char ps[1024]={0};
FILE *ptr;
strcpy(ps, cmd);
if((ptr=popen(ps, "r"))!=NULL)
{
while(fgets(buf_ps, 1024, ptr)!=NULL)
{
strcat(result, buf_ps);
if(strlen(result)>1024)
break;
}
pclose(ptr);
ptr = NULL;
}
else
{
printf("popen %s error\n", ps);
}
}
int main(int argc,char **argv)
{
//首先判断命令行参数是否正确
if(argc!=2){
printf("usage : ./caesar_decryption [filename]\n");
printf("\t eg. ./caesar_decryption out.txt");
}

char str[1025],filename[64];
for(int offset=1;offset<=26;offset++){
sprintf(filename,"right_%d.txt",offset);
freopen(argv[1],"r",stdin);
freopen(filename,"w",stdout);

while (cin.getline(str,sizeof(str))) {
int len=strlen(str);
for(int i=0;i<len;i++){
if(str[i]>='a'&&str[i]<='z'){
if((str[i]+offset)>'z')
cout<<(char)(str[i]+offset-26);
else
cout<<(char)(str[i]+offset);
continue;
}
if((str[i]>='A'&&str[i]<='Z')){
if((str[i]+offset)>'Z')
cout<<(char)(str[i]+offset-26);
else
cout<<(char)(str[i]+offset);
continue;
}
cout<<str[i];
}
}
cout<<str<<endl;
cin.clear(); //清除cin的状态标识符
}

//建个数组暴力筛一遍找到原来的明文
freopen("/dev/tty","w",stdout);
int num[28];
char command[64],result[64];
for(int offset=1;offset<=26;offset++){
sprintf(filename,"right_%d.txt",offset);
sprintf(command,"cat %s | aspell list | wc -l",filename);
result[0]='\0';
executeCMD(command,result);
//这里得到的result是以\n结尾的
num[offset] = atoi(result);
// num[offset] = strtol(result,nullptr,10);
// cout<<num[offset]<<endl;
}

//找到正确解密的那个输出
int minPos = 1,minNum=num[1];
for(int i=2;i<=26;i++){
if(num[i]<minNum){
minPos=i;
minNum=num[i];
}
}
printf("正确的解密明文是right_%d.txt\n在字典查不到的单词数为%d\n",minPos,num[minPos]);
}

解密前查看一下in.txt的内容,之前加密程序的结果
Caesar解密预览

然后进行解密,因为之前加密是右移3位,到这儿就是右移23位可以达到相同的结果,只有UTF在字典中没有收录,所以查不到的单词数为1
Caesar解密


参考资料

如果对您有帮助,请我喝杯咖啡?