strgeon

安全课-1-古典Caesar加密
前言这几天上计算机安全课,讲了点密码学的东西,这次的代码是在Ubuntu上用C++进行编写的。这次是非常简单的古典...
扫描右侧二维码阅读全文
10
2018/08

安全课-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,然后创建一个文件并写入测试字符。

$ 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系统语言是中文,所以修改配置文件

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

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

$ 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

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


加密程序

没啥好说,直接上代码

#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加密


解密

也直接上代码吧

#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解密


参考资料

最后修改:2018 年 08 月 10 日 03 : 52 PM
如果觉得我的文章对你有用,请随意赞赏

发表评论