首先说一下,这个程序的保密性不高= =!,就当练习玩。
最近刚刚学完双循环链表。就做了个这个
加密思路是,根据随机生成的密匙,对26字母进行移位;来选取密文。密匙大小为0~99;
程序比较简单,但是写完这个对我有很大的提高。
在调试过程中发现了许多自己的问题,比如说,加了循环没有加跳出去的条件。导致死循环,嗯,很多都是这个问题;
还有一个就是,在将密匙写入文件时,要将密匙以大写的字符形式的16进制保存。
然后我就想偷懒用fprintf来格式化直接写入文件16进制,随后就发现个问题;
就是当数值为0x0F时,他会将高位的0变成空白符,只写入F。
后来我就想把文件里的空格替换成字符’0’,但是呢问题又来了,每当我fseek设置文件位置指针的时候,总是会指向文件头;
然后用了各种方法,包括手动来修改指针,无果。。。。
后来要朋友试一下,他那没问题…没问题…然后我就凌乱了。
= =所以出来了现在这样的程序,我不用偷懒的方法了。
话说,个人觉得 进制转换那个函数的思想是最好的。
**************************************************************************************************************
2013-10-01更新…
搞了一天,现在已经支持大小写英文,数字,和其他字符。
关于小写的支持,是在大写字符处理的基础上增加了大小写转换(= =.这货就是想偷懒儿…)
刚开始写大小写转换的功能代码片段的时候,觉得重复性太强。就封装到函数里了,不过我怎么觉得封装之后比不封装更复杂?
在写数字支持的时候,发现自己的代码顺序摆放好不科学,属于一个功能区的东西都被分开了。
增加各种支持后,就不得不对于实现同功能的代码放到一起。
好吧,又有进步了。
下面贴文件,
顺序为:文档,函数头,身子。。
文档:
名称:Vigenere[维吉尼亚]
功能:用维吉尼亚方法对字符串进行加密/解密。
生成:程序运行后会根据加密解密选择,生成相应的文件。
文件内容:
if(加密)
{
将密文和密匙顺序交替写入文件;
例如:A(密文) + BF(密匙) = ABF
}
if(解密)
{
保存明文到文件;
}
需要的函数:
1. Slist Letter() [字母字典]
2. Slist Number() [数字字典]
2. char Matching(char plain, int key, Slist head, FLAG flag) [密文/明文匹配]
3. char upper_lower(FLAG *cflag, char letter) [大小写转换]
4. int convert(FLAG flag, char pword[2], int key) [进制转换]
5. void Plaintext() [对输入的明文保存到文件]
6. int Key(int seed) [随机生成密匙]
7. void Encryption(Slist Let_head, Slist Num_head) [加密]
8. void Decryption(Slist Let_head, Slist Num_head) [解密]
9. void UI() [简易界面]
注:加密/解密方法
1.将 密匙%26 取余;
2.使用字典进行匹配;
匹配方法:
用取余后的密匙,循环查找字符。
例如 密匙为2,明文为A, 那么密文就是C;
解密的方法和这个相反,密文为C,明文就为A;
密文的存储方法是 密文+密匙;
所以在进行解密的时候,每次取三个字符,计算密匙对密文进行解密;
程序大致运行流程/思路:
1.进入选择界面
2.用户输入选择后,转入相应的功能
(Encryption加密)
{
加密下有两个选项:
(1,直接输入明文)
{
将输入的明文保存到 plaintext.txt 文件;
直接转到下面
}
(2,从 plaintext.txt 的文件中获取明文)
{
while(非 文件尾){
生成随机密匙;
对密匙进行计算,获取文件中的明文进行加密
将密文保存到文件中;
将密匙转换成字符的16进制形式
将字符的16进制的密匙保存到密文后
}
删除 plaintext.txt 明文文件
}
}
(Decryption解密)
{
解密只能从此程序生成的文件中获取密文
{
分别获取密文和密匙;
对字符型密匙转换成数据型10进制密匙
计算密匙,对密文进行解密
将明文保存到文件
}
}
函数头:
#if 0
名称:vigenere.h
内容:程序所用到的定义以及函数;
1. Slist Letter() [字母字典]
2. Slist Number() [数字字典]
2. char Matching(char plain, int key, Slist head, FLAG flag) [密文/明文匹配]
3. char upper_lower(FLAG *cflag, char letter) [大小写转换]
4. int convert(FLAG flag, char pword[2], int key) [进制转换]
5. void Plaintext() [对输入的明文保存到文件]
6. int Key(int seed) [随机生成密匙]
7. void Encryption(Slist Let_head, Slist Num_head) [加密]
8. void Decryption(Slist Let_head, Slist Num_head) [解密]
9. void UI() [简易界面]
#endif
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define FLAG int
//用来存储字母的链表节点;
typedef struct list
{
char character;
struct list *prior;
struct list *next;
}List, *Slist;
/************************************************
*名称:Letter;
*调用:Slist Letter();
*作用:生成字母字典:26字母的双循环链表,用来进行匹配明文/密文;
*参数:无;
*返回值:链表的指针;
*************************************************/
Slist Letter()
{
Slist head, p, q;
char i = 'Z';
p = head = (Slist)malloc(sizeof(List));
head->character = i;
i--;
while (i >= 'A')
{
q = (Slist)malloc(sizeof(List));
q->character = i;
q->next = p;
p->prior = q;
p = q; //头插法
i--;
}
head->next = p;
p->prior = head;
head = p; //由于是头插法,建立时头在Z,所以将头节点移动到A;
return head;
}
/************************************************
*名称:Number;
*调用:Slist Number();
*作用:生成数字字典:0~9数字的双循环链表,用来进行匹配明文/密文;
*参数:无;
*返回值:链表的指针;
*************************************************/
Slist Number()
{
Slist head, p, q;
char i = '9';
p = head = (Slist)malloc(sizeof(List));
head->character = i;
i--;
while (i >= '0')
{
q = (Slist)malloc(sizeof(List));
q->character = i;
q->next = p;
p->prior = q;
p = q; //头插法
i--;
}
head->next = p;
p->prior = head;
head = p; //由于是头插法,建立时头在9,所以将头节点移动到0;
return head;
}
/***************************************************************
*名称:Matching;
*调用:char Matching(FLAG flag, char plain, int key, Slist head);
*作用:通过密匙,进行加密/解密;
*参数:plain,要匹配的明文/密文; key,密匙; head,字典头节点;
* flag,加密/解密状态;
*注:flag == 1 为加密,反之为解密;
*返回值:char word,匹配后的密文/明文;
****************************************************************/
char Matching(FLAG flag, char plain, int key, Slist head)
{
Slist p;
p = head;
while (p->character != plain) //匹配明文/密文
{
p = p->next;
}
if(flag == 1) //加密
{
while (key)
{
p = p->next;
key--;
}
}
else if(flag == 0) //解密
{
while(key)
{
p = p->prior;
key--;
}
}
else
{
printf("传入了错误的flag状态值!\n\n");
exit(1);
}
return p->character;
}
/********************************************************
*名称:upper_lower;
*调用:char upper_lower(FLAG *cflag, char letter);
*作用:大小写转换。
*参数:*cflag, 大小写状态符。
* if (*cflag == 0) 小写转大写;
* if (*cflag == 1) 大写转小写;
*
* letter, 需要进行转换的字母
*
*返回值:转换/未转换的字母 letter
********************************************************/
char upper_lower(FLAG *cflag, char letter)
{
if ( ('a' <= letter && letter <= 'z') && (*cflag == 0) )
{
*cflag = 1;
return letter - 32;
}
else if ( ('A' <= letter && letter <= 'Z') && (*cflag == 1) )
{
*cflag = 0;
return letter + 32;
}
else if (0 > *cflag || *cflag > 1)
{
printf("传入了错误的flag状态值!\n\n");
exit(1);
}
else
{
return letter; //非字母字符 或 真.大写字母;
}
}
/********************************************************
*名称:convert;
*调用:char convert(FLAG flag, char pword[2], int key);
*作用:进制转换。flag == 0{int 10d -> char 16h} ;
* flag == 1{char 16h -> int 10d};
*参数:flag, 函数功能选择状态符;
* if (flag == 0)
* {
* pword[2],将作为存储结果的容器;
* key,提供要转换的密匙;
* }
* else if (flag == 1)
* {
* pword[2],提供要转换的密匙字符;
* key,将作为结果存储的容器;
* }
*返回值:flag == 0,返回成功值1;flag == 1,返回10进制结果;
*PS:这个函数让我觉得C++的重载和模板是多么伟大的发明!
********************************************************/
int convert(FLAG flag, char pword[2], int key)
{
int num;
int i = 0;
if (flag == 1)
{
while (i < 2)
{
if(i == 0) //第一次计算高位
{
num = key/16;
}
else if (i == 1) //第二次计算低位
{
num = key%16;
}
if (0 <= num && num <= 9) //16进制不同形式的存储;
{
pword[i] = '0' + num;
}
else if (10 <= num && num <= 15)
{
pword[i] = 'A' + num - 10;
}
i++;
}
return 1;
}
else if (flag == 0)
{
i = 0;
while(i < 2) //获取密匙字符并转换成10进制数据;
{
if('0'<= pword[i] && pword[i] <= '9')
{
pword[i] = pword[i] - '0';
}
else if('A' <= pword[i] && pword[i] <= 'Z')
{
pword[i] = pword[i] - 'A' + 10;
}
i++;
}
key = pword[0] * 16 + pword[1]; //10进制密匙
return key;
}
else
{
printf("传入了错误的flag状态值!\n\n");
exit(1);
}
}
/*****************************************
*名称:Plaintext;
*调用:Plaintext();
*作用:提示用户输入要加密的字符,并生成相应的文件;
*参数:无;
*返回值:无;
*****************************************/
void Plaintext()
{
FILE *filetext;
errno_t err;
int i;
char temp;
i = 0;
err = fopen_s(&filetext, "plaintext.txt", "w"); //返回0表示无错,否则出错;
if(err)
{
printf("文件打开错误!");
exit(1);
}
printf("请输入明文(英文,数字),按回车键结束:"); //将明文写入文件;
while (1)
{
temp = getchar();
if (temp == '\n')
{
break;
}
if ( fputc(temp, filetext) == EOF)
{
printf("写入错误!");
exit(1);
}
//else
//{
// printf("输入错误,请输入大写英文,不包含其他字符:");
// fflush(stdin);
// continue;
//}
}
fclose(filetext);
printf("明文保存成功!\n");
}
/*****************************************
*名称:Key;
*调用:Key(int seed);
*作用:生成一个0~99的随机数;
*参数:seed,一个整数值;
*返回值:int型的随机数;
*说明:用时间函数作为种子,在运行很快的情况下,
避免产生相同的随机数,每次随机出加上个参数;
*****************************************/
int Key(int seed)
{
srand((unsigned)time(NULL));
return (rand() + seed)%100;
}
/*********************************************
*名称:Encryption;
*调用:Encryption(Slist Let_head, Slist Num_head);
*作用:从文件获取明文加密并保存到密文文件,删除明文文件;
*参数:Let_head,字母字典头节点;
Num_head,数字字典头节点;
*返回值:将生成保存密文的 encryption.txt 文件;
*********************************************/
void Encryption(Slist Let_head, Slist Num_head)
{
FILE *file_plantext, *file_encryption;
errno_t err; //文件读取/打开错误状态
char letter; //用来临时存储字符
char pword[2] = {0, 0}; //接收密匙转换结果
int key; //临时存储密匙
int seed; //随机数种子之一;
FLAG flag; //文件加密 和 10->16 所需要的状态符一样,只需要设置一次即可;
FLAG *cflag; //大小写状态符;
int i, ch;
ch = 0;
cflag = &ch;
flag = 1;
seed = 0;
err = fopen_s(&file_plantext, "plaintext.txt", "r");
if(err)
{
printf("读取 plaintext.txt 文件错误!\n\n");
exit(1);
}
err = fopen_s(&file_encryption, "encryption.txt", "w");
if(err)
{
printf("创建 encryption.txt 文件错误!\n\n");
exit(1);
}
while (!feof(file_plantext))
{
key = Key(seed); //取密匙
letter = fgetc(file_plantext); //从文件中获取明文
if (letter == -1)
{
break;
}
//如果是数字则....
if ('0' <= letter && letter <= '9')
{
letter = Matching(flag, letter, key%10, Num_head);
}
//如果是字母则....
else if ( ('a' <= letter && letter <= 'z') || ('A' <= letter && letter <= 'Z') )
{
letter = upper_lower(cflag, letter); //如,小写字母,将转换成大写字母形式进行处理
letter = Matching(flag, letter, key%26, Let_head); //加密字符
letter = upper_lower(cflag, letter); //若,小写字母被转换大写,则转换回小写
}
//其他字符则不用处理;
fputc(letter, file_encryption); //将密文字符写入文件
convert(flag, pword, key); //将密匙转换成16进制形式的字符
i = 0;
while (i < 2) //密匙是用两位字符存储的
{
fputc(pword[i], file_encryption);
i++;
}
seed++;
}
fclose(file_plantext);
fclose(file_encryption);
printf("密文成功保存到 encryption.txt 文件中!\n");
remove("plaintext.txt"); //删除保存明文的文件
printf("删除 plaintext.txt 明文文件!\n\n");
}
/*******************************************
*名称:Decryption;
*调用:Decryption(Slist Let_head, Slist Num_head);
*作用:从指定文件中获取密文和密匙,解密出明文保存到文件中;
*参数:Let_head,字母字典头节点;
Num_head,数字字典头节点;
*返回值:将结果回显到屏幕;
*******************************************/
void Decryption(Slist Let_head, Slist Num_head)
{
FILE *file_plaintext, *file_encryption;
errno_t err;
int key; //临时存储密匙
char letter, pword[2]; //letter,临时存储明文
int i, ch;
FLAG flag; //加密/解密状态符
FLAG *cflag; //大小写状态符
ch = 0;
cflag = &ch;
flag = 0;
key = 0;
err = fopen_s(&file_plaintext, "plaintext.txt", "w");
if(err)
{
printf("文件 plaintext.txt 创建失败!\n\n");
exit(1);
}
err = fopen_s(&file_encryption, "encryption.txt", "r");
if(err)
{
printf("文件 encryption.txt 读取失败!\n\n");
exit(1);
}
printf("明文为:");
while(!feof(file_encryption))
{
letter = fgetc(file_encryption); //获取密文
if (letter == -1)
{
break;
}
i = 0;
while (i < 2) //密匙是用两位字符存储的
{
pword[i] = fgetc(file_encryption);
i++;
}
key = convert(flag, pword, key); //对16进制密匙字符转换成10进制整型
if ('0' <= letter && letter <= '9')
{
letter = Matching(flag, letter, key%10, Num_head);
}
else if ( ('a' <= letter && letter <= 'z') || ('A' <= letter && letter <= 'Z') )
{
letter = upper_lower(cflag, letter);
letter = Matching(flag, letter, key%26, Let_head); //解密字符;
letter = upper_lower(cflag, letter);
}
fputc(letter, file_plaintext); //将明文写入文件
putchar(letter);
}
fclose(file_plaintext);
fclose(file_encryption);
printf("\n成功解密到 plaintext.txt 文件中!\n\n");
}
/*********************************
名称:UI;
调用:UI();
作用:在屏幕上打印出一个简易的界面;
参数:无;
返回值:无;
*********************************/
void UI()
{
printf("----------------------------------------------------------------\n");
printf("\t\t欢迎使用维吉尼亚字符加密解密系统\t");
printf("\n\n");
printf("\n这是一个加密字符串的小程序,您可以通过这个程序来获得加密后的密文。\n");
printf("1.直接输入字符串进行加密\n");
printf("2.通过 plaintext.txt 文件获取明文,进行加密\n");
printf("3.解密 encryption.txt 文件中的字符串\n");
printf("4.查看作者信息\n");
printf("5.退出\n");
printf("\n请选择:");
}
main:
#include "vigenere.h"
int main()
{
Slist letter_head, number_head;
char choose;
letter_head = Letter(); //字典
number_head = Number();
UI(); //界面
while (1)
{
scanf_s("%1c", &choose, 1);
fflush(stdin); //清理键盘缓冲区,避免多余字符对程序造成影响;
switch (choose)
{
case '1': //保存明文到文件
Plaintext();
case '2':
Encryption(letter_head, number_head); //加密
printf("请选择:");
break;
case '3':
Decryption(letter_head, number_head); //解密
printf("请选择:");
break;
case '4':
printf("\n\n");
printf("作者:MeeSong\n");
printf("博客:http://blog.csdn.net/meesong \n\n");
printf("请选择:");
break;
case '5':
printf("\n谢谢使用!");
exit(1);
default:
printf("输入错误,请重新输入:");
fflush(stdin);
break;
}
}
}