密码学: Vigenere 密码法实现文件加密与解密 (C 语言)_明文为shijiazhuang采用26字母进行位移代换,其中密钥为k=key,求加密后密文是-程序员宅基地

技术标签: C programming language  

题目:

文件的传输会有明文和密文的区别,明文发送是不安全的。本题目实现对文件的加密和解密操作,采用的加密算法是根据密钥将明文中的字母置换为其它字母,所有字母不区分大小写,不考虑除英文字母外的字符。例如:明文是:They will arrive tomorrow,密钥k=Monday,具体加密过程如下:

① 设置英文字母与0到25有如下的对应关系:
在这里插入图片描述
②依据上述对应关系将明文和密钥转化为一组数字:
k=(12,14,13,3,0,24)
m=(19,7,4,24,22,8,11,11,0,17,17,8,21,4,19,14,12,14,17,17,14,22)
③将明文数字依据密钥长度分段,并逐一与密钥数字相加(模26),得到密文数字,即:

在这里插入图片描述
C=(5,21,17,1,22,6,23,25,13,20,17,6,7,18,6,17,12,12,3,5,1,25)

④依据字母和数字对应关系将密文数字转换为字母串,即密文为:
c=FVRBWGXZNURGHSGRMMDFBZ
解密过程与加密过程类似,采用的是减运算模26。

功能要求:

主函数提供功能菜单供用户选择,用户可以选择调用以下各个功能,也可以选择退出程序。系统应提供以下功能:
(1) 加密:对给定文件file1.txt内容按照密钥k=Monday进行加密,加密后密文写到文件file2.txt中;
(2) 解密:对给定密文文件file3.txt 利用密钥k=Monday进行解密,解密后的明文存放在文件file4.txt中;
(3) 破解密钥的长度:对给定密文文件file5.txt,搜索长度>=3且出现次数>=3的相同密文段keyi,将这些相同密文段及其出现的次数写入文件file6.txt。

例如密文若为:hksabcdiukoabcdhkslslabcdpphkslll,则相同密文段及其出现的次数为:hks *3 abc *3 abcd *3 bcd *3 。


实现代码:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>

/*宏定义秘钥和模,数组容量, 结构体数组大小*/
#define Key "Monday"
#define Mod 26
#define MAXSIZE 1000
#define SubStrMaxSize 1000000

/*定义子串结构体*/
struct subString {
    
    char str[MAXSIZE]; /*子串名称*/
    int sum;    /*子串出现次数*/
} subStr[SubStrMaxSize]; /*定义结构体数组个数*/

/*定义全局变量*/
char alphabetUpper[26] = {
    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
                          'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
                          'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
                          'Y', 'Z'}; /*单词对照表大写*/
char alphabetLower[26] = {
    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
                          'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
                          'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
                          'y', 'z'};/*单词对照表小写*/

/*文件读取*/
void readFile(FILE *fp, char *buff) {
    
    int i = 0;
    char c;
    while (1) {
    
        c = fgetc(fp);
        // 去空格, EOF 结束
        if (c != EOF && c != ' ') {
    
            buff[i++] = c;
        } else if (c == EOF){
    
            break;
        }
    }
    fclose(fp);
}

/*文件写入*/
void writeFile(FILE *fp, char *buff) {
    
    int i = 0;
    char c;
    while ((c = buff[i])!= '0') {
    
        fputc(c, fp);
        i++;
    }
    fclose(fp);
}

/*获取整型数组长度*/
/*因为数组作为参数传参时,无法再使用 sizeof 计算长度了,所以抽取出一个公共函数*/
int getIntLength(int intArr[]) {
    
    int *p;
    int len = 0;
    p = intArr;
    while (p != NULL && *p != -1) {
    
        len++;
        p++;
    }
    return len;
}

/*获取字符数组有效长度, '0' 为无效值*/
int getCharLength(char charArr[]) {
    
    char *p;
    int len = 0;
    p = charArr;
    /*必须要再加一个限定条件 *p != '\0', 否则计算字符串长度会出现 bug, 长度几百 */
    while (p != NULL && *p != '0' && *p != '\0') {
    
        len++;
        p++;
    }
    return len;
}


/*字母转数字*/
void alpToNum (char *buff, int *num) {
    
    int index = 0;
    for (int i = 0; i < getCharLength(buff); ++i) {
    
        for (int j = 0; j < strlen(alphabetLower); ++j) {
    
            if (buff[i] == alphabetUpper[j] || buff[i] == alphabetLower[j]) {
    
                num[index++] = j;
                break;
            }
        }
    }
}

/*数字转字母*/
void numToAlp(int *num, char *buff) {
    
    int index = 0;
    for (int i = 0; i < getIntLength(num); ++i) {
    
        buff[index++] = alphabetUpper[num[i]];
    }
}

/*加密过程*/
void encryption(int *clearNum, int *keyNum, char *encryptText) {
    
    int index = 0;
    /*待加密数组和秘钥数组的长度*/
    int clearNumLen = getIntLength(clearNum);
    int keyNumLen = getIntLength(keyNum);

    /*存放加密后的整形数组*/
    int EncryptNum[MAXSIZE];
    memset(EncryptNum, -1, sizeof(int)*1000);

    /*存储 i 到数组结尾的距离*/
    int dist;
    /*逐一与密钥数字相加(模26)操作*/
    for (int i = 0; i < clearNumLen; i+= keyNumLen) {
    
        dist = clearNumLen - i;
        /*当dist大于秘钥长度则一一对应相加模26加运算处理,当 dist 小于 秘钥长度则遍历前 dist 个的秘钥一直相加模运算*/
        if (dist > keyNumLen) {
    
            for (int j = 0; j < keyNumLen; ++j) {
    
                // 加密
                EncryptNum[index++] = (clearNum[j + i] + keyNum[j]) % Mod;
            }
        } else {
    
            for (int j = 0; j < dist; ++j) {
    
                // 加密
                EncryptNum[index++] = (clearNum[j + i] + keyNum[j]) % Mod;
            }
        }
    }
    /*整形数组转换成字符数组*/
    numToAlp(EncryptNum, encryptText);
}

/*解密过程*/
void decryption(char *encryptedText, int *keyNum, char *decryptText) {
    
    int index = 0;

    /*已加密数组和秘钥数组的长度*/
    int encryptedTextLen = getCharLength(encryptedText);
    int keyNumLen = getIntLength(keyNum);

    /*存放已加密字符数组转化后的数字*/
    int encryptedNum[MAXSIZE];
    memset(encryptedNum, -1, sizeof(int) * 1000);

    /*字母转数字*/
    alpToNum(encryptedText, encryptedNum);

    /*存放明文数字数组*/
    int clearNum[MAXSIZE];
    memset(clearNum, -1, sizeof(int) * MAXSIZE);

    /*存储 i 到数组结尾的距离*/
    int dist;

    /*存储秘钥和密文各个数字的差值*/
    int diffVal;

    /*存放 x - y[x/y] 的值*/
    int resVal;

    /*逐一减运算再模运算*/
    /*取余和模运算有区别:可能你会觉得数学中的 余数(remainder) 其实就是 取模(mod)
      x%y = x - y[x/y]
      虽然数学中的余数概念和我们的计算机中的余数概念一致,但实现却不一致,对于正数,二者计算出的
      结果是相等的,但是负数就不相等了。*/
    for (int i = 0; i < encryptedTextLen; i+= keyNumLen) {
    
        /*解密*/
        dist = encryptedTextLen - i;
        if (dist > keyNumLen) {
    
            for (int j = 0; j < keyNumLen; ++j) {
    
                diffVal = encryptedNum[j + i] - keyNum[j];
                resVal  = ceil(fabs(((double)diffVal / (double)Mod)));
                /*被除数小于 0, [x/y] 向下取整 */
                if (diffVal < 0) {
    
                    /*取绝对值再向上取整(计算机默认向下取整,导致差异,我们需要向上取整), 再转回负数*/
                    clearNum[index++] = diffVal + (Mod * resVal);
                } else {
    
                    /*res > 0 正常处理即可*/
                    clearNum[index++] = (encryptedNum[j + i] - keyNum[j]) % Mod;
                }
            }
        } else {
    
            /*解密*/
            for (int j = 0; j < dist; ++j) {
    
                diffVal = encryptedNum[j + i] - keyNum[j];
                resVal  = ceil(fabs(((double)diffVal / (double)Mod)));
                /*被除数小于 0, [x/y] 向下取整 */
                if (diffVal < 0) {
    
                    /*取绝对值再向上取整(计算机默认向下取整,导致差异,我们需要向上取整),再转回负数*/
                    clearNum[index++] = diffVal + (Mod * resVal);
                } else {
    
                    /*res > 0 正常处理即可*/
                    clearNum[index++] = (encryptedNum[j + i] - keyNum[j]) % Mod;
                }
            }
        }
    }

    /*整形数组转换成字符数组*/
    numToAlp(clearNum, decryptText);
}


/*破解秘钥长度*/
void crackSecretLen(char *secretKeyText, char *cipherSection) {
    
    int index; /*结构体数组下标*/
    int i; /*该行字符串的最大长度*/
    int j; /*这个子串的最大长度*/
    int k; /*遍历的初始下标*/
    int z = 0; /*k的偏移量*/
    char TempSection[MAXSIZE]; /*定义临时存放每个长度>=3且出现次数>=3的相同密文段*/

    /*获取一个函数的所有子串存入结构体数组, 排除空串*/
    for (i = getCharLength(secretKeyText); i >= 1; i--) {
    
        for (j = 1; j <= i ; ++j) {
    
            /*定义一个临时数组存放字符*/
            char tempChar[j+1];
            for (k = 0; k < j; ++k) {
    
                /*字符存入,生成子串字符数组*/
                tempChar[k] = secretKeyText[k + z];
            }
            /*踩坑!!: strcpy 必须复制必须是字符串,所以必须 tempChar 最后添加 \0, 否则乱码*/
            tempChar[j] = '\0';
            /*设立一个标志位, 为 0 说明不存在相同的,为 1 说明存在相同的*/
            int flag = 0;
            /*判断是否子串已存在,存在则原基础 sum + 1, 否则添加子串*/
            for (int l = 0; l < index+1; ++l) {
    
                /*字符比较,相等则存在,原基础 sum + 1*/
                if (strcmp(tempChar, subStr[l].str) == 0) {
    
                    subStr[l].sum++;
                    /*标志位改变,不存入*/
                    flag = 1;
                    break;
                }
            }
            /*不存在相同的字符串,则数据存入结构体*/
            if (!flag) {
    
                /*存入*/
                strcpy(subStr[index].str, tempChar);
                /*个数设为1*/
                subStr[index].sum = 1;
                /*自增*/
                index++;
                /*复位 0*/
                flag = 0;
            }
        }
        z++;
    }
    /*搜索出长度>=3且出现次数>=3的相同密文段*/
    for (int m = 0; m < index; ++m) {
    
        if (getCharLength(subStr[m].str) >= 3 && subStr[m].sum >= 3) {
    
            sprintf(TempSection,"%s * %d  ", subStr[m].str, subStr[m].sum);
            strcat(cipherSection, TempSection);
        }
    }
}

int main() {
    

    /*选择操作*/
    int choice;

    /*
    * 秘钥转数字操作
    * */
    /*存放Key转换后的数字*/
    int keyNum[MAXSIZE];
    memset(keyNum, -1, sizeof(int) * MAXSIZE);
    alpToNum(Key, keyNum);

    while(1) {
    
        printf("1--加密1\n");
        printf("2--解密2\n");
        printf("3--破解秘钥长度3\n");
        printf("0--退出\n");
        printf("your choice(0/1/2/3):");
        scanf("%d", &choice);
        switch (choice) {
    
            case 1: {
    
                /*
                * 明文转数字操作
                * */
                FILE *file1 = fopen("D:\\OrderProject\\Encryption\\file1.txt", "r");
                /*存放明文file1.txt里面读取出的字符串*/
                char clearText[MAXSIZE];
                /*初始化为 '0'*/
                memset(clearText, '0', sizeof(char) * MAXSIZE);
                /*存放 clearText[] 转换后的数字*/
                int clearNum[MAXSIZE];
                /*设默认值为 -1, 不在区间 [0,25] 即可, 显然取 -1 最佳*/
                /*直接 clearNum[1000] = {-1} 是无法赋初值的,而需使用 memset*/
                memset(clearNum, -1, sizeof(int) * MAXSIZE);
                readFile(file1, clearText);
                alpToNum(clearText, clearNum);

                /*
                * 加密操作
                * */
                /*存放加密后的字符串*/
                char encryptText[MAXSIZE];
                memset(encryptText, '0', sizeof(char) * MAXSIZE);
                encryption(clearNum, keyNum, encryptText);

                /*
                 * 加密数据写入文件中
                 * */
                FILE *file2 = fopen("D:\\OrderProject\\Encryption\\file2.txt", "w");
                writeFile(file2, encryptText);
            };
                break;
            case 2: {
    
                /*
                * 解密操作
                * */
                FILE *file3 = fopen("D:\\OrderProject\\Encryption\\file3.txt", "r");
                /*存放已加密file3.txt里面读取出的字符串*/
                char encryptedText[MAXSIZE];
                memset(encryptedText, '0', sizeof(char) * MAXSIZE);
                /*存放 encryptedText[] 转换后的数字*/
                int encryptedNum[MAXSIZE];
                memset(encryptedNum, -1, sizeof(int) * MAXSIZE);
                readFile(file3, encryptedText);
                alpToNum(encryptedText, encryptedNum);

                /*
                 * 解密操作
                 * */
                char decryptText[MAXSIZE];
                memset(decryptText, '0', sizeof(char) * MAXSIZE);
                decryption(encryptedText, keyNum, decryptText);

                /*
                 * 解密数据写入文件中
                 * */
                FILE *file4 = fopen("D:\\OrderProject\\Encryption\\file4.txt", "w");
                writeFile(file4, decryptText);
            };
                break;
            case 3: {
    
                /*
                * 破解密钥长度操作
                */
                FILE *file5 = fopen("D:\\OrderProject\\Encryption\\file5.txt", "r");
                /*存放file5里面读取出的密钥字符串*/
                char secretKeyText[MAXSIZE];
                /*初始化 '0'*/
                memset(secretKeyText, '0', sizeof(char) * MAXSIZE);
                /*定义存放长度>=3且出现次数>=3的相同密文段的字符数组*/
                char cipherSection[MAXSIZE];

                /*读取file5文件数据*/
                readFile(file5, secretKeyText);
                crackSecretLen(secretKeyText, cipherSection);

                /*
                 * 秘钥字段写入文件中
                 * */
                FILE *file6 = fopen("D:\\OrderProject\\Encryption\\file6.txt", "w");
                fputs(cipherSection, file6);
            }
                break;
            default:
                exit(0);
        }
    }
}
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/haduwi/article/details/117677488

智能推荐

c# 调用c++ lib静态库_c#调用lib-程序员宅基地

文章浏览阅读2w次,点赞7次,收藏51次。四个步骤1.创建C++ Win32项目动态库dll 2.在Win32项目动态库中添加 外部依赖项 lib头文件和lib库3.导出C接口4.c#调用c++动态库开始你的表演...①创建一个空白的解决方案,在解决方案中添加 Visual C++ , Win32 项目空白解决方案的创建:添加Visual C++ , Win32 项目这......_c#调用lib

deepin/ubuntu安装苹方字体-程序员宅基地

文章浏览阅读4.6k次。苹方字体是苹果系统上的黑体,挺好看的。注重颜值的网站都会使用,例如知乎:font-family: -apple-system, BlinkMacSystemFont, Helvetica Neue, PingFang SC, Microsoft YaHei, Source Han Sans SC, Noto Sans CJK SC, W..._ubuntu pingfang

html表单常见操作汇总_html表单的处理程序有那些-程序员宅基地

文章浏览阅读159次。表单表单概述表单标签表单域按钮控件demo表单标签表单标签基本语法结构<form action="处理数据程序的url地址“ method=”get|post“ name="表单名称”></form><!--action,当提交表单时,向何处发送表单中的数据,地址可以是相对地址也可以是绝对地址--><!--method将表单中的数据传送给服务器处理,get方式直接显示在url地址中,数据可以被缓存,且长度有限制;而post方式数据隐藏传输,_html表单的处理程序有那些

PHP设置谷歌验证器(Google Authenticator)实现操作二步验证_php otp 验证器-程序员宅基地

文章浏览阅读1.2k次。使用说明:开启Google的登陆二步验证(即Google Authenticator服务)后用户登陆时需要输入额外由手机客户端生成的一次性密码。实现Google Authenticator功能需要服务器端和客户端的支持。服务器端负责密钥的生成、验证一次性密码是否正确。客户端记录密钥后生成一次性密码。下载谷歌验证类库文件放到项目合适位置(我这边放在项目Vender下面)https://github.com/PHPGangsta/GoogleAuthenticatorPHP代码示例://引入谷_php otp 验证器

【Python】matplotlib.plot画图横坐标混乱及间隔处理_matplotlib更改横轴间距-程序员宅基地

文章浏览阅读4.3k次,点赞5次,收藏11次。matplotlib.plot画图横坐标混乱及间隔处理_matplotlib更改横轴间距

docker — 容器存储_docker 保存容器-程序员宅基地

文章浏览阅读2.2k次。①Storage driver 处理各镜像层及容器层的处理细节,实现了多层数据的堆叠,为用户 提供了多层数据合并后的统一视图②所有 Storage driver 都使用可堆叠图像层和写时复制(CoW)策略③docker info 命令可查看当系统上的 storage driver主要用于测试目的,不建议用于生成环境。_docker 保存容器

随便推点

网络拓扑结构_网络拓扑csdn-程序员宅基地

文章浏览阅读834次,点赞27次,收藏13次。网络拓扑结构是指计算机网络中各组件(如计算机、服务器、打印机、路由器、交换机等设备)及其连接线路在物理布局或逻辑构型上的排列形式。这种布局不仅描述了设备间的实际物理连接方式,也决定了数据在网络中流动的路径和方式。不同的网络拓扑结构影响着网络的性能、可靠性、可扩展性及管理维护的难易程度。_网络拓扑csdn

JS重写Date函数,兼容IOS系统_date.prototype 将所有 ios-程序员宅基地

文章浏览阅读1.8k次,点赞5次,收藏8次。IOS系统Date的坑要创建一个指定时间的new Date对象时,通常的做法是:new Date("2020-09-21 11:11:00")这行代码在 PC 端和安卓端都是正常的,而在 iOS 端则会提示 Invalid Date 无效日期。在IOS年月日中间的横岗许换成斜杠,也就是new Date("2020/09/21 11:11:00")通常为了兼容IOS的这个坑,需要做一些额外的特殊处理,笔者在开发的时候经常会忘了兼容IOS系统。所以就想试着重写Date函数,一劳永逸,避免每次ne_date.prototype 将所有 ios

如何将EXCEL表导入plsql数据库中-程序员宅基地

文章浏览阅读5.3k次。方法一:用PLSQL Developer工具。 1 在PLSQL Developer的sql window里输入select * from test for update; 2 按F8执行 3 打开锁, 再按一下加号. 鼠标点到第一列的列头,使全列成选中状态,然后粘贴,最后commit提交即可。(前提..._excel导入pl/sql

Git常用命令速查手册-程序员宅基地

文章浏览阅读83次。Git常用命令速查手册1、初始化仓库git init2、将文件添加到仓库git add 文件名 # 将工作区的某个文件添加到暂存区 git add -u # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,不处理untracked的文件git add -A # 添加所有被tracked文件中被修改或删除的文件信息到暂存区,包括untracked的文件...

分享119个ASP.NET源码总有一个是你想要的_千博二手车源码v2023 build 1120-程序员宅基地

文章浏览阅读202次。分享119个ASP.NET源码总有一个是你想要的_千博二手车源码v2023 build 1120

【C++缺省函数】 空类默认产生的6个类成员函数_空类默认产生哪些类成员函数-程序员宅基地

文章浏览阅读1.8k次。版权声明:转载请注明出处 http://blog.csdn.net/irean_lau。目录(?)[+]1、缺省构造函数。2、缺省拷贝构造函数。3、 缺省析构函数。4、缺省赋值运算符。5、缺省取址运算符。6、 缺省取址运算符 const。[cpp] view plain copy_空类默认产生哪些类成员函数

推荐文章

热门文章

相关标签