密码学: Vigenere 密码法实现文件加密与解密 (C 语言)

本文介绍了一种基于字母置换的加密算法,通过明文文件file1.txt的加密(使用密钥Monday)并写入file2.txt,同时提供了解密功能,将密文file3.txt还原为明文file4.txt,还演示了如何通过频繁出现的密文段破解密钥长度。
摘要由CSDN通过智能技术生成

题目:

文件的传输会有明文和密文的区别,明文发送是不安全的。本题目实现对文件的加密和解密操作,采用的加密算法是根据密钥将明文中的字母置换为其它字母,所有字母不区分大小写,不考虑除英文字母外的字符。例如:明文是: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);
        }
    }
}
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

m0rta1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值