一、密码学的基本概念
1、信息安全的基本任务
甲乙两方需要通过网络远程通信,互相传递机密信息,由于网络是一个开放式空间,如果存在"敌手"丙对甲乙两方的通信进行攻击,或者窃取传递的机密信息,而甲乙之间的通信没有任何保护,是相当危险的

2、信息安全的基本属性
(1)机密性:保证信息为授权者使用,并且不泄漏给未经授权者,通俗地说就是"敌手"看不到或者看不懂。
(2)认证:分为消息认证和身份认证,消息认证负责保证消息来源的真实性,身份认证负责确保通信实体的真实性。
(3)完整性:分为数据完整性和系统完整性,数据完整性即数据未被未经授权者篡改或损坏,系统完整性即信息系统未被非授权操纵,按既定的功能运行。
(4)不可否认性:无论是发送方和接收方,都不能抵赖所传输的信息。
(5)可靠性:特定行为和结果的一致性,也可认为传递的信息不能有二义性。
(6)可用性:保证信息和信息系统随时为授权者提供服务,不能出现非授权者滥用或对授权者拒绝服务的情况。
(7)可控性:授权实体可以控制信息系统和信息使用的特性。
(8)审计:确保实体的活动可被跟踪。
3、什么是密码学
(1)信息安全中的四个基本属性可由密码学保证。
①机密性:研究如何使得信息只有授权者能够解读,而非授权者不能解读。
②认证:研究如何确保(或者说验证)信息的来源正确,如何保证(或者说验证)通信实体的真实性。
③完整性:研究如何确保(或者说验证)数据在传输过程中没有被篡改。
④不可否认性:研究如何确保用户行为的不可否认性,即不能抵赖所传递的信息。
(2)密码学的应用需要基于密码算法和协议。
4、密码算法
(1)密码算法的基本概念:
①明文M是未经过加密处理的数据,密文C是经过加密处理后的数据,密钥k是秘密参数。
②加密函数负责使用密钥k对明文M进行加密,得到密文C()。
③解密函数负责使用密钥k对密文C进行解密,得到明文M()。
(2)密码算法的需求:
①对算法的使用者可逆,算法的使用者可以将密文恢复为明文。
②对敌手不可逆,敌手无法将密文恢复成明文。
(3)密码算法实际上是一个带有秘密参数的函数,知道秘密参数,求逆非常容易,不知道秘密参数,求逆极其困难。
(4)保密通信系统模型:

(5)密码算法的分类:
①按照功能分类:
加密算法:用于机密性解决方案,保证数据不被泄漏
杂凑函数:用于完整性解决方案,保证数据不被篡改
数字签名:用于认证和不可否认性解决方案
②按照密钥的使用方式分类:
对称密钥密码体制:加密密钥与解密密钥相同,如分组密码、流密码
非对称密钥密码体制:加密密钥与解密密钥不同,如公钥加密、数字签名
二、密码学发展简史
1、密码学发展时间轴概览

2、古典密码
(1)时间区域:从由人类诞生以来到1949年。
(2)这一时期的密码学还不是科学,而是艺术。
(3)该时期内出现一些密码算法和加密设备,出现密码算法设计的基本手段(代替法和置换法)。
(4)该时期内数据的保密基于加密算法的保密。
3、现代密码
(1)现代密码I阶段:Shannon发表"The Communication Theory of SecretSystems",定义了理论安全性,提出扩散和混淆原则,奠定了密码学的理论基础,实现艺术到科学的转变。该阶段数据的安全基于密钥而不是算法的保密。
(2)现代密码II阶段:1976年Diffie & Hellman的"New DirectionsCryptography"提出了公钥密码的概念;1977年Rivest、Shamir & Adleman提出了RSA公钥算法;1977年,DES成为了第一代公开的、完全说明细节的商业级密码标准;90年代逐步出现椭圆曲线等其它公钥算法。公钥密码部分解决了对称密钥密码算法密钥共享和密钥管理困难的问题。
(3)现代密码III阶段:1994年,Shor提出量子计算机模型下分解大整数和求解离散对数的多项式时间算法;2000年,AES正式取代DES,成为了新的加密标准;2006年,第一届后量子密码学国际研讨会召开;2017年,NIST开始征集后量子密码标准。
三、密码分析学
1、密码学科分支
(1)密码学分为密码分析学和密码编码学。
(2)密码编码学是设计和实现密码系统的一门科学,目的是通过加密、解密、数字签名等手段,实现信息的机密性、完整性、身份认证和不可否认性。
①目标:构建安全的通信系统,确保信息在传输或存储过程中不被非法访问或篡改。
②主要技术:对称加密(如AES)、非对称加密(如RSA)、哈希函数(如SHA-256)、数字签名、密钥管理、身份认证机制。
(3)密码分析学是研究如何破解密码系统的一门科学,目的是在不知道密钥或未授权的情况下,获取加密信息的内容或系统弱点。
①目标:评估密码系统的安全性,找出其漏洞或弱点,从而改进密码系统。
②主要技术:频率分析、差分分析、线性分析、暴力破解、侧信道攻击(如时间分析、功耗分析)、选择明文攻击、选择密文攻击、已知明文攻击等。
(4)密码分析学和密码编码学的区别:
|------|------------------|------------------|
| | 密码编码学 | 密码分析学 |
| 目标 | 构建安全的密码系统 | 破解密码系统或评估其安全性 |
| 角色 | 信息安全守护者 | 信息安全攻击者 |
| 应用场景 | 用于加密通信、数据保护、身份认证 | 用于安全测试、漏洞分析、系统加固 |
| 方法 | 设计算法、协议、密钥管理机制 | 分析算法、寻找漏洞、攻击系统 |
| 目的 | 保护信息 | 获取信息或改进保护机制 |
(5)密码分析学和密码编码学的联系:
①密码编码学的发展离不开密码分析学的挑战。只有在密码分析的不断攻击下,密码编码学才能不断完善和改进。
②密码分析学是评估密码系统安全性的重要手段,通过密码分析,可以验证一个密码系统是否真的"安全"。
③密码分析学的成果往往反过来推动密码编码学的进步。
④虽然两者方法不同,但最终目标都是为了提高信息系统的安全性。

2、密码分析学的前提
(1)密码体制的安全性仅依赖于对密钥的保密,而不应依赖于算法的保密。
(2)根据Kerckhoffs假设,密码分析学的前提是假定密码分析者和敌手知道所使用的密码系统,即知道所使用的加密算法,知道明文的概率分布规律,知道密钥的概率分布规律,甚至能够拿到加密装置进行分析等,那么破解的关键就是密钥。
3、密码体制的攻击方法
(1)穷举攻击:通过试遍所有的密钥来进行破译。可增大密钥的数量进行应对。
(2)统计分析攻击:通过分析密文和明文的统计规律来破译。可设法使明文和密文的统计规律不一样进行应对。
(3)解密变换攻击:针对加密变换的数学基础,通过数学求解设法找到解密变换。可选用具有坚实的数学基础和足够复杂的加密算法进行应对。
4、四种密码破译方法
(1)唯密文攻击(CiphertextOlny Attack):
①条件:密码分析者仅知道一些密文。
②最困难的一种方法,一般是穷举搜索,对截获密文用所有可能密钥去尝试破译。
③敌手使用唯密文攻击,说明敌手知道的信息量最少,最易抵抗。
④只要有足够的计算时间和存储容量,唯密文攻击原则上可百分百成功,但在实际上一种能保证安全要求的实用密码算法,都会设计得这一方法在实际上不可行。
⑤一般的敌手需要对密文进行统计测试分析,为此需要知道被加密的明文类型、英文文本、图象等。
(2)已知明文攻击(Known Plaintext Attack):
①条件:密码分析者知道一些明文和相应的密文。
②在很多情况下,敌手可能有更多的信息,也许能够截获一个或多个明文及其对应的密文,或消息中将出现某种明文格式,这时的攻击称为已知明文攻击,敌手也许能从已知的明文被变换成密文的方式得到密钥。
(3)选择明文攻击(ChosenPlaintext Attack):
①条件:密码分析者可以选择一些明文,并得到相应的密文。
②如果攻击者能在加密系统中插入自己选择的明文消息,则通过该明文消息对应的密文有可能确定出密钥的结构。
(4)选择密文攻击(ChosenCiphertext Attack)
①条件:密码分析者可以选择一些密文,并得到相应的明文。
②攻击者利用解密算法,对自己所选的密文解密出相应的明文,有可能确定出密钥信息。
5、无条件安全和计算上安全
(1)无条件安全(不可破译的),指的是无论截获多少密文,都没有足够信息来唯一确定明文,则该密码是无条件安全的,即对算法的破译不比猜测有优势。
(2)计算上安全,指的是使用有效资源对一个密码系统进行分析而未能破译,则该密码是强的或计算上安全的。
(3)密码算法需要满足以下两条准则之一,满足的密码算法在实际中是可用的。
①破译密文的代价超过被加密信息的价值。
②破译密文所花的时间超过信息的有用期。
四、古典密码算法
1、置换密码
(1)置换密码就是对明文字符或字符组进行位置移动的密码,明文的字母保持相同,但顺序被打乱了。

(2)如下是列换位密码的C语言实现。
列换位密码的工作流为,将明文按行写入一个固定列数的矩阵(例如列数由密钥长度决定),按照密钥指定的列顺序(数字序列)逐列读取字符,得到密文,解密时,将密文按列写入矩阵(列顺序与加密时相同),再按行读出明文
cpp
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#define MAX_LEN 100
#define FILL_CHAR 'X'
// 加密函数
void encrypt(const char *plain, const int *key, int key_len, char *cipher) {
int len = strlen(plain);
int rows = (len + key_len - 1) / key_len; // 计算行数(向上取整)
char matrix[rows][key_len]; //若不支持,则需改成动态内存分配
// 初始化矩阵为填充字符(假设为X)
for (int i = 0; i < rows; i++) {
for (int j = 0; j < key_len; j++) {
matrix[i][j] = FILL_CHAR;
}
}
// 按行填充明文
int idx = 0;
for (int i = 0; i < rows && idx < len; i++) {
for (int j = 0; j < key_len && idx < len; j++) {
matrix[i][j] = plain[idx++];
}
}
// 按密钥顺序读取列
int pos = 0;
for (int k = 0; k < key_len; k++) {
int col = key[k] - 1; // 密钥从1开始,转换为0索引
for (int i = 0; i < rows; i++) {
cipher[pos++] = matrix[i][col];
}
}
cipher[pos] = '\0';
}
// 解密函数
void decrypt(const char *cipher, const int *key, int key_len, char *plain) {
int len = strlen(cipher);
int rows = len / key_len; // 密文长度必定是key_len的倍数
char matrix[rows][key_len]; //若不支持,则需改成动态内存分配
// 按密钥顺序将密文填入各列
int idx = 0;
for (int k = 0; k < key_len; k++) {
int col = key[k] - 1;
for (int i = 0; i < rows; i++) {
matrix[i][col] = cipher[idx++];
}
}
// 按行读取得到明文(去除填充字符)
idx = 0;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < key_len; j++) {
if (matrix[i][j] != FILL_CHAR) {
plain[idx++] = matrix[i][j];
}
}
}
plain[idx] = '\0';
}
// 辅助函数:将字符串密钥转换为数字数组(如 "3 1 4 2" -> [3,1,4,2])
int parse_key(const char *key_str, int *key) {
char temp[MAX_LEN];
strcpy(temp, key_str);
int count = 0;
char *token = strtok(temp, " ");
while (token != NULL) {
key[count++] = atoi(token);
token = strtok(NULL, " ");
}
return count;
}
int main() {
char plain[MAX_LEN], cipher[MAX_LEN], decrypted[MAX_LEN];
char key_str[MAX_LEN];
int key[MAX_LEN];
printf("输入明文: ");
fgets(plain, MAX_LEN, stdin);
plain[strcspn(plain, "\n")] = 0; // 去除换行
printf("输入密钥(数字序列,空格分隔,例如 3 1 4 2): ");
fgets(key_str, MAX_LEN, stdin);
key_str[strcspn(key_str, "\n")] = 0;
int key_len = parse_key(key_str, key);
// 加密
encrypt(plain, key, key_len, cipher);
printf("密文: %s\n", cipher);
// 解密
decrypt(cipher, key, key_len, decrypted);
printf("解密后: %s\n", decrypted);
return 0;
}
2、代替密码
(1)代替密码就是构造一个或多个密文字母表,然后用密文字母表中的字母或者字母组来代替明文字母或字母组,各字母或字母组的相对位置不变,但其本身的值改变了。
(2)代替密码算法针对英文字母进行处理,首先将26个字母与十进制数字中的0~25一一对应,如下表所示。代替密码的加密函数与解密函数中,数的加法和乘法都定义为模26的加法和乘法。

(3)代替密码分为单表代替密码和多表代替密码。
①单表代替密码又分为加法密码、乘法密码和仿射密码。


②多表代替密码基于仿射密码进行延伸而得。

(4)如下是维吉尼亚密码(典型的多表代替密码)的C语言实现。
密钥是一个单词或短语,每个字母对应一个移位值(A=0、B=1、...、Z=25)
加密时,将明文字母按对应密钥字母的移位值进行凯撒移位(将明文中的每个英文字母在字母表中向后移动k位(k为密钥),超出Z时循环回到A),密钥循环使用
解密时,将密文字母按对应密钥字母的移位值进行反向移位
通常只处理字母,非字母字符保持不变,且大小写敏感(即大写字母移位后仍大写,小写字母移位后仍小写)
cpp
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAX_LEN 1024
// 将密钥转换为移位值数组(0-25),只考虑字母,忽略大小写
int prepare_key(const char *key, int shifts[]) {
int len = 0;
for (int i = 0; key[i] != '\0'; i++) {
if (isalpha(key[i])) {
shifts[len++] = toupper(key[i]) - 'A'; // 统一转为大写计算移位
}
}
return len; // 返回有效密钥长度
}
// 对单个字符进行加密/解密操作
char shift_char(char ch, int shift, int encrypt) {
if (!isalpha(ch)) return ch; // 非字母不变
char base = isupper(ch) ? 'A' : 'a'; // 先确定大小写基准,然后进行模26的加减运算
int offset = ch - base;
if (encrypt) {
offset = (offset + shift) % 26;
}
else {
offset = (offset - shift + 26) % 26; // 保证非负
}
return base + offset;
}
// 维吉尼亚加密
void vigenere_encrypt(const char *plaintext, const char *key, char *ciphertext) {
int shifts[MAX_LEN];
int key_len = prepare_key(key, shifts);
if (key_len == 0) {
printf("错误:密钥必须包含至少一个字母!\n");
strcpy(ciphertext, plaintext);
return;
}
int key_idx = 0;
for (int i = 0; plaintext[i] != '\0'; i++) {
char c = plaintext[i];
if (isalpha(c)) {
ciphertext[i] = shift_char(c, shifts[key_idx % key_len], 1);
key_idx++; // 仅当处理字母时才消耗密钥
}
else {
ciphertext[i] = c; // 非字母直接复制
}
}
ciphertext[strlen(plaintext)] = '\0';
}
// 维吉尼亚解密
void vigenere_decrypt(const char *ciphertext, const char *key, char *plaintext) {
int shifts[MAX_LEN];
int key_len = prepare_key(key, shifts);
if (key_len == 0) {
printf("错误:密钥必须包含至少一个字母!\n");
strcpy(plaintext, ciphertext);
return;
}
int key_idx = 0;
for (int i = 0; ciphertext[i] != '\0'; i++) {
char c = ciphertext[i];
if (isalpha(c)) {
plaintext[i] = shift_char(c, shifts[key_idx % key_len], 0);
key_idx++;
}
else {
plaintext[i] = c;
}
}
plaintext[strlen(ciphertext)] = '\0';
}
int main() {
char plaintext[MAX_LEN], key[MAX_LEN], ciphertext[MAX_LEN], decrypted[MAX_LEN];
printf("维吉尼亚密码演示\n");
printf("请输入明文: ");
fgets(plaintext, MAX_LEN, stdin);
plaintext[strcspn(plaintext, "\n")] = 0; // 去除换行符
printf("请输入密钥 (字母组成): ");
fgets(key, MAX_LEN, stdin);
key[strcspn(key, "\n")] = 0;
// 加密
vigenere_encrypt(plaintext, key, ciphertext);
printf("加密后的密文: %s\n", ciphertext);
// 解密
vigenere_decrypt(ciphertext, key, decrypted);
printf("解密后的明文: %s\n", decrypted);
return 0;
}