如何使用C语言实现Vigenère密码加解密
在洛谷看到这题,觉得有意思,遂写。
参考文章:C语言实现Vigenere加解密算法(附带源码)
1. Vigenère密码简介与原理
Vigenère密码是一种多表密码,使用一系列凯撒密码(Caesar cipher)来实现加密。在Vigenère密码中,明文中的每个字符都被替换为密文中的对应字符,而密钥用于确定使用哪个凯撒密码表。具体来说,明文中的每个字符都会根据密钥中的字符进行位移,从而生成密文。如图:
加密例子:明文:HELLO,密钥:KEY,加密后:RIJVS
对照表中,先将密钥填充至与明文长度相同:KEYKE。然后在对照表中找到明文字符对应的密文字符,如第一个字符在对照表中的K行找到H列,对应的字符是R,于是第一个字符就被加密成R。以此类推,直到明文全部加密完成。
实际操作中,其实就是一系列的不同的凯撒密码加密,而密钥决定了使用哪个凯撒密码表,这里不赘述。我们把字母转换成数字(A = 0,B = 1,……),写成公式可以表达成:
加密:C = (P + K)% 26
其中C为密文字符,P为明文字符,K为密钥字符。同样可以得到解密公式:
解密:P = (C - K + 26)% 26
2.具体实现
这里仅给出函数实现,以及省略了引入string.h和ctype.h头文件。
我们可以考虑先忽略字母的大小写,解密后再对照原文判断是否转换大小写。先实现字母转数字、数字转字母的函数:
int Char_to_Number(char c)
{return c - 'A';
}char Number_to_Char(int num, char origin)
{char c = 'A' + num;//* 对照原文if (islower(origin)){return tolower(c);}return c;
}
解码:
char *Decode(char *key, char *C, char *M)
{for (int i = 0, j = 0; i < strlen(C); i++){if (isalpha(C[i])){int keyNum = Char_to_Number(key[j % strlen(key)]); // 填充密钥int CNum = Char_to_Number(C[i]);int MNum = (CNum - keyNum + 26) % 26;M[i] = Number_to_Char(MNum, C[i]);// 不能在循环体里面写j++😡,否则遇到非字母字符j也会增加j++; }else{M[i] = C[i];}}return M;
}
加密:
char *Encode(char *key, char *M, char *C)
{for (int i = 0, j = 0; i < strlen(M); i++){if (isalpha(M[i])){int keyNum = Char_to_Number(key[j % strlen(key)]);int MNum = Char_to_Number(M[i]);int CNum = (MNum + keyNum) % 26;C[i] = Number_to_Char(CNum, M[i]);j++;}else{C[i] = M[i];}}
}
完整测试用例代码:
#include <stdio.h>
#include <string.h>
#include <ctype.h>int Char_to_Number(char c);
char Number_to_Char(int num, char origin);
char *Decode(char *key, char *C, char *M);
char *Encode(char *key, char *M, char *C);int main()
{char key[101];char C[1001];char M[1001];fgets(key, sizeof(key), stdin);fgets(C, sizeof(C), stdin);key[strcspn(key, "\n")] = '\0';C[strcspn(C, "\n")] = '\0';Decode(key, C, M);printf("%s\n", M);// Encode(key, M, C);// printf("%s\n", C);return 0;
}int Char_to_Number(char c)
{return toupper(c) - 'A';
}char Number_to_Char(int num, char origin)
{char c = 'A' + num;//* 对照原文if (islower(origin)){return tolower(c);}return c;
}char *Decode(char *key, char *C, char *M)
{for (int i = 0, j = 0; i < strlen(C); i++){if (isalpha(C[i])){int keyNum = Char_to_Number(key[j % strlen(key)]); // 填充密钥int CNum = Char_to_Number(C[i]);int MNum = (CNum - keyNum + 26) % 26;M[i] = Number_to_Char(MNum, C[i]);j++; // 不能在循环体里面写j++😡,否则遇到非字母字符j也会增加}else{M[i] = C[i];}}return M;
}char *Encode(char *key, char *M, char *C)
{for (int i = 0, j = 0; i < strlen(M); i++){if (isalpha(M[i])){int keyNum = Char_to_Number(key[j % strlen(key)]);int MNum = Char_to_Number(M[i]);int CNum = (MNum + keyNum) % 26;C[i] = Number_to_Char(CNum, M[i]);j++;}else{C[i] = M[i];}}
}
3. 总结
知道了加解密公式,实现起来就很简单,以及注意需要对照原文判断转换字母的大小写。