维吉尼亚字符串加密解密小程序

首先说一下,这个程序的保密性不高= =!,就当练习玩。

最近刚刚学完双循环链表。就做了个这个

加密思路是,根据随机生成的密匙,对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;
		}
	}
}

 

    原文作者:维吉尼亚加密问题
    原文地址: https://blog.csdn.net/MeeSong/article/details/12195941
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞