C语言实现英文字符的凯撒加密/读取文件/写入文件/密码验证

前言

本篇文章主要对txt文件中的每行内容通过使用数字key,按照key*key的方式往后循环加密,并将加密后的内容写入encrpted.txt中。

加密规则

使用 密钥的平⽅ 对英⽂字⺟表向后执⾏,例如,明⽂ T 将使⽤(-3)*(-3)向后旋转加密为 K 。
ABCDEFGHIJKLMNOPQRSTUVWXYZ

加密只能在[a,z] ,[A,Z]的范围内识别和加密明文中有意义的字符。所有其他未加密的字符应保留为原始明文。

未加密(不是英文字符)的字符也应写⼊ encrypted.txt ⽂件中。

所有加密字符都应保留在普通字符的原始范围内,例如,由密钥的平⽅(-1)*(-1)加密的原字符 ‘A’ 将变成 ‘Z’,⽽不是’@'。相同的密码规则也适⽤于[a,z]中的普通字符。

技术要点

  1. 如何在程序运行前安全地进行密码验证;
  2. C语言按行读取文件
  3. C语言按行写入文件
  4. 对读取的行中的英文字符([a,z]和[A,Z])进行凯撒加密
  5. 判断一个数是否过大(超过计算机long long显示的范围)
  6. 如何判断读取的行是否为空(NULL)
  7. 程序运行时运行设置工作目录

问题描述

  1. 使用密码身份验证来编写循环密码程序。
  2. 当程序启动时,密码请求应⽴即显示出来。
  3. 若密码验证正确,在控制台中打印“Password OK!\n”,并执⾏加密 plain.txt⽂件。加密完成后,在控制台中打印“Encryption COMPLETE.\n”,并安全退出程序。
  4. 假设密码和来自 keys.txt 的加密密钥是机密的,并且来⾃ plain.txt 的数据是绝密的,因此不希望密码,加密密钥和数据使用后在内存中留下任何痕迹。
  5. 若密码验证失败,在控制台中打印“Password FAILED.\n”,并安全退出程序。
  6. 加密时,使⽤密钥的平⽅,即 key*key 来加密来⾃plain.txt的数据。如果 keys.txt中的key 太⼤,请跳过该key取下⼀个密钥,并取下⼀个明⽂句⼦。
  7. 如果在plain.txt中遇到 空的明⽂句⼦,跳过该句取下⼀个句⼦并取下⼀个键。
  8. 将加密后的句⼦保存到 encrypted.txt ⽂件中。每个加密句⼦的⻓度应于明⽂句⼦的⻓度相等。
  9. 该程序应⽀持环境变量 WORKING_PATH
    a. 如果设置了环境变量,使⽤该环境变量来访问 plain.txt, encrypted.txt 和 keysN.txt⽂件
    b. 如果没有设置环境变量,使⽤当前⼯作⽬录来访问 plain.txt, encrypted.txt 和 keysN.txt⽂件

思路整理

在做一个有多个技术点的项目时,动手之前可以先画出整个项目的流程图,这样在后续代码实现时不会遗漏某个要求,也不会忘了下一步要做什么了。接下来是使用Visio所作的流程图。
在这里插入图片描述

代码实现

密码验证

本实验要求保证密码是安全的,因此最好的做法就是使用动态分配内存的方式验证用户输入的密码是否正确。无论正确还是错误,均应在控制台中提示。

  1. 程序启动时,提示用户输入密码;
  2. 正确密码是10位,用户在控制台输入密码时按回车代表输入完毕
  3. 写验证密码函数时,可以将户输入的密码以及密码的长度作为参数。验证时,可以先对比密码的长度,如果不是10位,可以直接返回false。
  4. 为了确保密码安全,应该做到正确的密码不是常量,而是每次调用密码验证函数时动态分配的内存里存放正确的密码。
  5. 密码验证函数的返回值,这里设成匹配正确返回1,匹配错误返回0。

代码如下:

  1. main函数
int main(int argc, char** argv) {
    
    
	char p[PASSWORDLEN];
	int i = 0;
	char c;
	
	printf("password(10 bits):"); //提示用户输入密码

	while ((c = getchar()) != '\n') {
    
    
		p[i++] = c;
		if (i > 9) break; // to prevent array subscript out of index
	}
	int pwLen = sizeof(p) / sizeof(p[0]);
	int pwIsCorrect = passwordAuthentication(p, pwLen);
	// password is correct
	if (pwIsCorrect == 1) {
    
    
		printf("Password OK!\n");
	}
	// wrong password
	else {
    
    
		printf("Password FAILED.\n");
		exit(0);
	}
	printf("Encryption COMPLETE.\n");

	return 0;
}

  1. 密码验证函数passwordAuthentication
int passwordAuthentication(char pw[], int length) {
    
    
	int i = 0;
	if (length != 10) {
    
    
		return 0;
	}
	//动态分配内存,保存正确的密码
	char* tmpPass = (char*)malloc(sizeof(char) * 10);
	tmpPass[0] = '2';
	tmpPass[1] = '0';
	tmpPass[2] = '2';
	tmpPass[3] = '1';
	tmpPass[4] = '2';
	tmpPass[5] = '6';
	tmpPass[6] = '3';
	tmpPass[7] = '9';
	tmpPass[8] = '3';
	tmpPass[9] = '4';
	for (i; i < 10; i++) {
    
    
		if (tmpPass[i] != pw[i])
		{
    
    
			free(tmpPass);
			return 0;
		}
	}
	free(tmpPass);
	return 1;
}

加密函数encryption()

由于明文和要解密的key存放在两个txt文件中,还需要将加密过后的密文存放到第三个txt文件中。因此,涉及到文件的读和写。

  1. 从plain.txt和keys.txt中读取明文和key时,一行plain可能很长,因此涉及到动态内存的分配。每次最多分配128个字节,如果不够的话可以多申请几次
  2. 每次申请内存后要释放
  3. 读取key时,由于key值很大,且有正有负。因此首先将key值使用char* 类型的变量存放。再将char * 转成long long 类型。由于unsigned long long 可表示的数据范围较大,因此将正值使用unsigned long long 类型的变量存放,负值使用long long 存放。判断正值和负值只需判断char *类型的数组的第一个字符是否是’-'即可。为方便判断key使用long long存放时是否溢出,使用"errno.h"头文件,如果程序中不发生错误,errno的值为0,如果溢出(发生错误),errno的值会改变。
  4. 对字符加密时,使用key* key向后循环加密。由于key值很大,key * key的值则会更大。因此,根据(a * b)mod c = (a mod c)* (b mod c)特性,可以将key值对于26取模之后相乘再做循环加密

读取文件单行 函数

int get_line(char **str, int *buff_size, FILE *fp) {
    
    
	if (NULL == *str) {
    
    
		//default size 128 bytes
		*buff_size = 128;
		*str = malloc(128);
	}
	//empty string
	memset(*str, 0, strlen(*str));
	//Record the number of characters per line
	int line_ch_size = 0;
	//characters per read
	int ch;
	while (1)
	{
    
    
		if ((ch = fgetc(fp)) != EOF) {
    
    
			if (ch == '\n') {
    
    
				//Finished reading a line
				break;
			}
			else {
    
    
				if (line_ch_size == (*buff_size) - 1) {
    
    
					//Need to expand, add 128 at a time
					*buff_size = (*buff_size) + 128;
					*str = realloc(*str, *buff_size);
				}
				sprintf(*str, "%s%c", *str, ch);
				line_ch_size++;
			}
		}
		else {
    
    
			line_ch_size = -1;
			break;
		}
	}
	return line_ch_size;
}

释放内存函数

void get_line_free(char **str){
    
    
	if (NULL != *str) {
    
    
		free(*str);
	}
}

字符加密函数

void cipher_cyclic_rotations(char** encryption_res, char** origin_key, char** plain_line)
{
    
    
	errno = 0;
	//a pointer point to a char in plain_line
	char* is_valid_ch;
	long long negative_key;
	unsigned long long positive_key;
	char* tmp_key_ch;
	//rotation count
	long long cycles;
	int char_required;
	int char_to_a_len = 0;
	is_valid_ch = *plain_line;
	if (*is_valid_ch == '\0') {
    
    
		*encryption_res = NULL;
		return;
	}
	if (*origin_key[0] == '-') {
    
    
		negative_key = strtoll(*origin_key, NULL, 10);
		cycles = ((negative_key % 26) * (negative_key % 26)) % 26;
	}
	else {
    
    
		positive_key = strtoull(*origin_key, NULL, 10);
		cycles = ((positive_key % 26) * (positive_key % 26)) % 26;
	}
	// errno means key exceeded maximum display range
	if (errno != 0) {
    
    
		// printf("value %s exceeded maximum display range\n", *origin_key);
		*encryption_res = NULL;
		return;
	}
	//cycles: rotation count
	for (; *is_valid_ch != '\0'; is_valid_ch++) {
    
    
		if ((*is_valid_ch >= 'a' && *is_valid_ch <= 'z')) {
    
    
			//the distance between the char and 'a'
			char_to_a_len = *is_valid_ch - 'a';
			if (char_to_a_len >= cycles) {
    
    
				*is_valid_ch = *is_valid_ch - cycles;
			}
			else {
    
    
				*is_valid_ch = 'z' - (cycles - 1 - char_to_a_len);
			}
		}
		if ((*is_valid_ch >= 'A' && *is_valid_ch <= 'Z')) {
    
    
			char_to_a_len = *is_valid_ch - 'A';
			if (char_to_a_len >= cycles) {
    
    
				*is_valid_ch = *is_valid_ch - cycles;
			}
			else {
    
    
				*is_valid_ch = 'Z' - (cycles - 1 - char_to_a_len);
			}
		}
	}
	*encryption_res = *plain_line;
}

加密函数

void encryption(char* workspace_path) {
    
    
	char* plain_path = "plain.txt";
	char* keys10_path = "keys10.txt";
	char* encrypted_path = "encrypted.txt";
	char* plain_path_origin = malloc(128);
	char* key_path_origin = malloc(128);
	char* encrypted_path_origin = malloc(128);
	if (workspace_path != NULL) {
    
    
		strcpy(plain_path_origin, workspace_path);
		strcpy(key_path_origin, workspace_path);
		strcpy(encrypted_path_origin, workspace_path);
		plain_path = strcat(plain_path_origin, "plain.txt");
		keys10_path = strcat(key_path_origin, "keys10.txt");
		encrypted_path = strcat(encrypted_path_origin, "encrypted.txt");
	}
	//open files
	FILE* fp = fopen(plain_path, "r");
	FILE* fp_en = fopen(encrypted_path, "w");
	FILE* fp_key = fopen(keys10_path, "r");
	char* encryption_res = NULL; //String to be written to encrypted.txt file after encryption
	//String to store each line
	char *str_line = NULL;
	//The size of the memory to store the string
	int buff_size;
	int buff_size_key;
	//characters per line
	int line_ch_size;
	int count = 0;
	long long tmp_key;
	char* origin_key = NULL;
	//file open failed
	if (!fp) {
    
    
		printf("main.c: 251, fopen() failed: %s file open failed.\n", plain_path);
		exit(0);
	}
	if (!fp_en) {
    
    
		printf("main.c: 255, fopen() failed: %s file open failed.\n", encrypted_path);
		exit(0);
	}
	if (!fp_key) {
    
    
		printf("main.c: 259, fopen() failed: %s file open failed\n", keys10_path);
		exit(0);
	}

	while (1) {
    
    
		encryption_res = NULL;
		//read a line form plain.txt
		line_ch_size = get_line(&str_line, &buff_size, fp);
		if (line_ch_size == -1 && str_line[0] == '\0') {
    
    
			break;
		}
		//read a line form keysN.txt
		get_line(&origin_key, &buff_size_key, fp_key);
		// Get the result that need write into enctypted.txt. If the result is NULL,then doesn't write into enctypted.txt
		cipher_cyclic_rotations(&encryption_res, &origin_key, &str_line);
		if (encryption_res == NULL) {
    
    
			continue;
		}
		//If the result is not NULL, write it into enctypted.txt
		fprintf(fp_en, "%s\n", encryption_res);
	}
	//free the malloced memory size
	get_line_free(&str_line);
	get_line_free(&origin_key);
	free(plain_path_origin);
	free(key_path_origin);
	free(encrypted_path_origin);
	//close the file
	fclose(fp);
	fclose(fp_en);
	fclose(fp_key);
}

设置工作路径

C语言中的main函数是可以携带参数的,设置的方式如下:
int main(int argc, char** argv)
因此可以将工作路径作为参数传给main函数。
在终端运行main函数的方式如下:
在cmd中直接敲入:
程序名【空格】参数1【空格】 参数2【空格】… 参数n【enter键结束键入】
或者
程序名.exe【空格】参数1【空格】 参数2【空格】… 参数n【enter键结束键入】
这里只将工作路径作为参数传给main函数,因此只有一个参数,是string类型。同时要判断所传的工作路径的长度,这里设置为,如果工作路径长于128个字符后,就提示错误。
设置工作路径的main函数如下:

int main(int argc, char** argv) {
    
    
	char p[PASSWORDLEN];
	int i = 0;
	char c;

	if (argv[1] != NULL) {
    
    
		int path_len = strlen(argv[1]);
		if (path_len > 128) {
    
    
			printf("main.c: 56, main() failed: %s ,the length of the path is too long.\n", argv[1]);
			exit(0);
		}
		else
		{
    
    
			printf("The current working path is %s.\n", argv[1]);
		}
	}
	else
	{
    
    
		printf("The current working path is the program running path\n");
		printf("You can change the path to other directories by adding runtime parameters when running the program.\n");
	}
	printf("password(10 bits):");

	while ((c = getchar()) != '\n') {
    
    
		p[i++] = c;
		if (i > 9) break; // to prevent array subscript out of index
	}
	int pwLen = sizeof(p) / sizeof(p[0]);
	int pwIsCorrect = passwordAuthentication(p, pwLen);
	// password is correct
	if (pwIsCorrect == 1) {
    
    
		printf("Password OK!\n");
		// Start encrypting files
		encryption(argv[1]);
	}
	// wrong password
	else {
    
    
		printf("Password FAILED.\n");
		exit(0);
	}
	printf("Encryption COMPLETE.\n");

	return 0;
}

总体代码

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <string.h>
#include <limits.h>
#include <math.h>
#include <errno.h>

#define PASSWORDLEN  10//password length

/*
 * @brief match password
 * @param pw password to match
 * @param length length of password to match
 * @return int When the password is correct, return 1; when the password is wrong, return 0
 */
int passwordAuthentication(char pw[], int length);

/*
 * @brief Get the line that need write into enctypted.txt. If the line is NULL,then doesn't write into enctypted.txt
 * @param encryption_res one line need to be written into enctypted.txt
 * @param origin_key one line read from keysN.txt
 * @param plain_line one line read from plain.txt
 */
void cipher_cyclic_rotations(char** encryption_res, char** origin_key, char** plain_line);

/*
 * @brief Get the Read key and plain object
 * @param str
 * @param size
 * @param fp
 * @return int Number of characters per line read
 */
int get_line(char **str, int *buff_size, FILE *fp); 

/*
 * @brief Free the key and plain object
 * @param str
 */
void get_line_free(char **str);

/*
 * @brief encryption function
 * @param workspace_path 
 */
void encryption(char* workspace_path);

int main(int argc, char** argv) {
    
    
	char p[PASSWORDLEN];
	int i = 0;
	char c;

	if (argv[1] != NULL) {
    
    
		int path_len = strlen(argv[1]);
		if (path_len > 128) {
    
    
			printf("main.c: 56, main() failed: %s ,the length of the path is too long.\n", argv[1]);
			exit(0);
		}
		else
		{
    
    
			printf("The current working path is %s.\n", argv[1]);
		}
	}
	else
	{
    
    
		printf("The current working path is the program running path\n");
		printf("You can change the path to other directories by adding runtime parameters when running the program.\n");
	}
	printf("password(10 bits):");

	while ((c = getchar()) != '\n') {
    
    
		p[i++] = c;
		if (i > 9) break; // to prevent array subscript out of index
	}
	int pwLen = sizeof(p) / sizeof(p[0]);
	int pwIsCorrect = passwordAuthentication(p, pwLen);
	// password is correct
	if (pwIsCorrect == 1) {
    
    
		printf("Password OK!\n");
		// Start encrypting files
		encryption(argv[1]);
	}
	// wrong password
	else {
    
    
		printf("Password FAILED.\n");
		exit(0);
	}
	printf("Encryption COMPLETE.\n");

	return 0;
}

int passwordAuthentication(char pw[], int length) {
    
    
	int i = 0;
	if (length != 10) {
    
    
		return 0;
	}
	char* tmpPass = (char*)malloc(sizeof(char) * 10);
	tmpPass[0] = '2';
	tmpPass[1] = '0';
	tmpPass[2] = '2';
	tmpPass[3] = '1';
	tmpPass[4] = '2';
	tmpPass[5] = '6';
	tmpPass[6] = '3';
	tmpPass[7] = '9';
	tmpPass[8] = '3';
	tmpPass[9] = '4';
	for (i; i < 10; i++) {
    
    
		if (tmpPass[i] != pw[i])
		{
    
    
			free(tmpPass);
			return 0;
		}
	}
	free(tmpPass);
	return 1;
}

void cipher_cyclic_rotations(char** encryption_res, char** origin_key, char** plain_line)
{
    
    
	errno = 0;
	//a pointer point to a char in plain_line
	char* is_valid_ch;
	long long negative_key;
	unsigned long long positive_key;
	char* tmp_key_ch;
	//rotation count
	long long cycles;
	int char_required;
	int char_to_a_len = 0;
	is_valid_ch = *plain_line;
	if (*is_valid_ch == '\0') {
    
    
		*encryption_res = NULL;
		return;
	}
	if (*origin_key[0] == '-') {
    
    
		negative_key = strtoll(*origin_key, NULL, 10);
		cycles = ((negative_key % 26) * (negative_key % 26)) % 26;
	}
	else {
    
    
		positive_key = strtoull(*origin_key, NULL, 10);
		cycles = ((positive_key % 26) * (positive_key % 26)) % 26;
	}
	// errno means key exceeded maximum display range
	if (errno != 0) {
    
    
		// printf("value %s exceeded maximum display range\n", *origin_key);
		*encryption_res = NULL;
		return;
	}
	//cycles: rotation count
	for (; *is_valid_ch != '\0'; is_valid_ch++) {
    
    
		if ((*is_valid_ch >= 'a' && *is_valid_ch <= 'z')) {
    
    
			//the distance between the char and 'a'
			char_to_a_len = *is_valid_ch - 'a';
			if (char_to_a_len >= cycles) {
    
    
				*is_valid_ch = *is_valid_ch - cycles;
			}
			else {
    
    
				*is_valid_ch = 'z' - (cycles - 1 - char_to_a_len);
			}
		}
		if ((*is_valid_ch >= 'A' && *is_valid_ch <= 'Z')) {
    
    
			char_to_a_len = *is_valid_ch - 'A';
			if (char_to_a_len >= cycles) {
    
    
				*is_valid_ch = *is_valid_ch - cycles;
			}
			else {
    
    
				*is_valid_ch = 'Z' - (cycles - 1 - char_to_a_len);
			}
		}
	}
	*encryption_res = *plain_line;
}

int get_line(char **str, int *buff_size, FILE *fp) {
    
    
	if (NULL == *str) {
    
    
		//default size 128 bytes
		*buff_size = 128;
		*str = malloc(128);
	}
	//empty string
	memset(*str, 0, strlen(*str));
	//Record the number of characters per line
	int line_ch_size = 0;
	//characters per read
	int ch;
	while (1)
	{
    
    
		if ((ch = fgetc(fp)) != EOF) {
    
    
			if (ch == '\n') {
    
    
				//Finished reading a line
				break;
			}
			else {
    
    
				if (line_ch_size == (*buff_size) - 1) {
    
    
					//Need to expand, add 128 at a time
					*buff_size = (*buff_size) + 128;
					*str = realloc(*str, *buff_size);
				}
				sprintf(*str, "%s%c", *str, ch);
				line_ch_size++;
			}
		}
		else {
    
    
			line_ch_size = -1;
			break;
		}
	}
	return line_ch_size;
}

void get_line_free(char **str){
    
    
	if (NULL != *str) {
    
    
		free(*str);
	}
}

void encryption(char* workspace_path) {
    
    
	char* plain_path = "plain.txt";
	char* keys10_path = "keys10.txt";
	char* encrypted_path = "encrypted.txt";
	char* plain_path_origin = malloc(128);
	char* key_path_origin = malloc(128);
	char* encrypted_path_origin = malloc(128);
	if (workspace_path != NULL) {
    
    
		strcpy(plain_path_origin, workspace_path);
		strcpy(key_path_origin, workspace_path);
		strcpy(encrypted_path_origin, workspace_path);
		plain_path = strcat(plain_path_origin, "plain.txt");
		keys10_path = strcat(key_path_origin, "keys10.txt");
		encrypted_path = strcat(encrypted_path_origin, "encrypted.txt");
	}
	//open files
	FILE* fp = fopen(plain_path, "r");
	FILE* fp_en = fopen(encrypted_path, "w");
	FILE* fp_key = fopen(keys10_path, "r");
	char* encryption_res = NULL; //String to be written to encrypted.txt file after encryption
	//String to store each line
	char *str_line = NULL;
	//The size of the memory to store the string
	int buff_size;
	int buff_size_key;
	//characters per line
	int line_ch_size;
	int count = 0;
	long long tmp_key;
	char* origin_key = NULL;
	//file open failed
	if (!fp) {
    
    
		printf("main.c: 251, fopen() failed: %s file open failed.\n", plain_path);
		exit(0);
	}
	if (!fp_en) {
    
    
		printf("main.c: 255, fopen() failed: %s file open failed.\n", encrypted_path);
		exit(0);
	}
	if (!fp_key) {
    
    
		printf("main.c: 259, fopen() failed: %s file open failed\n", keys10_path);
		exit(0);
	}

	while (1) {
    
    
		encryption_res = NULL;
		//read a line form plain.txt
		line_ch_size = get_line(&str_line, &buff_size, fp);
		if (line_ch_size == -1 && str_line[0] == '\0') {
    
    
			break;
		}
		//read a line form keysN.txt
		get_line(&origin_key, &buff_size_key, fp_key);
		// Get the result that need write into enctypted.txt. If the result is NULL,then doesn't write into enctypted.txt
		cipher_cyclic_rotations(&encryption_res, &origin_key, &str_line);
		if (encryption_res == NULL) {
    
    
			continue;
		}
		//If the result is not NULL, write it into enctypted.txt
		fprintf(fp_en, "%s\n", encryption_res);
	}
	//free the malloced memory size
	get_line_free(&str_line);
	get_line_free(&origin_key);
	free(plain_path_origin);
	free(key_path_origin);
	free(encrypted_path_origin);
	//close the file
	fclose(fp);
	fclose(fp_en);
	fclose(fp_key);
}

猜你喜欢

转载自blog.csdn.net/weixin_43943476/article/details/125866784
今日推荐