本博文借鉴自书本《密码编码学与网络安全——原理与实践(第七版)》,由William Stallings著,王后珍、李莉等译。
一、相关基础概念
1.广泛使用的密码算法与协议可分为以下四个主要领域:
- 对称加密: 加密任意大小的数据块或数据流的内容,包括消息、文件、加密秘钥和口令。
- 非对称加密: 加密小数据块,如加密密钥或数字签名中使用的散列函数值。
- 数据完整性算法:保护数据块的内容不被修改。
- 认证协议: 基于密码算法设计的认证方案,用来认证实体的身份。
2.其中的对称加密,也称传统加密或单钥加密,是20世纪70年代公钥密码产生之前唯一的加密类型,其五个基本组成:
- 明文: 原始可理解的消息或数据,是算法的输入。
- 加密算法:加密算法对明文进行各种代替和变换。
- 密钥: 密钥也是加密算法的输入,密钥独立于明文和算法。算法根据所用的特定密钥而产生不同的输出,算法所用的确切代替和变换也依赖密钥。
- 密文: 作为算法输出,看起来完全随机而杂乱的消息,依赖于明文和密钥。对于给定的消息,不同的密钥产生不同的密文,密文看上去是随机的数据流,并且其意义是不可理解的。
- 解密算法:本质上是加密算法的逆运算,输入密文和密钥,输出原始明文。
3.密码编码学系统所具有的的三个独立特征:
- 转换明文为密文的运算类型:所有的加密算法都基于两个原理:代替和置换。代替是将明文中每个元素映射成另一个元素;置换是将明文中的元素重新排列。上述运算的基本要求是不允许有信息丢失的(即所有的运算都是可逆的)。大多数密码体制,也称为乘积密码系统,都使用了多层代替和置换。
- 所用的密钥数:如果发送方和接收方使用相同的密钥,这种密码就称为对称密码、单密钥密码、秘密钥密码或传统密码。否则称为非对称密码、双钥或公钥密码。
- 处理明文的方法:分组密码每次处理输入的一组元素,相应地输出一组元素。流密码则是连续地处理输入元素,每次输出一个元素。
4.密码分析学和穷举攻击:
- 密码分析学:密码分析学攻击依赖于算法的性质、明文的一般特征或者某些明文对。企图利用算法的特征来推导出特定的明文或使用的密钥。
- 穷举攻击:攻击者对一条密文尝试所有可能的密钥直到把它转化为可读的、有意义的的明文。平均而言,获得成功至少要尝试所有密钥可能密钥地一半。
攻击类型 | 秘密分析者已知信息 | 说明 |
唯 密文 攻击 |
| 难度最大(甚至不知道加密算法)、最容易防范。 若密钥空间比较小,可采用穷举攻击。 否则运用各种统计方法对密文分析(需要对明文类型有所了解)。 |
已知 明文 攻击 |
| 与其相关的是可能词攻击。 若攻击者处理的是特定的信息,他就可能知道其中的某些关键词及其位置。 |
选择 明文 攻击 |
| 若分析者能过获得信源系统,让发送方在发送的信息中插入由他选择的信息,那么就可能实现选择明文攻击。 |
选择 密文 攻击 |
| 很少用到 |
选择 文本 攻击 |
| 很少用到 |
5.加密体制的无条件安全:
若一个密码体制满足:无论有多少可使用密文,都无法唯一的确认密文所对应的明文,则称该加密体制是无条件安全的。
即无论花费多少时间,攻击者都无法将密文解密,这仅仅是因为其所需的信息不在密文里。除了一次一密之外,所有的加密算法都不是无条件安全的。
因此加密算法的使用者应该挑选尽量满足以下标准的算法:
- 破译密码的代价超过密文信息的价值。
- 破译密码的时间超出密文信息的有效生命期。
二、代替技术
所有的加密技术都要用到的两个基本模块:代替和置换。代替技术是将明文字母替换成其他字母、数字或符号的方法。
1.Caesar(凯撒)密码
原理:对字母表中的每个字母,用它之后的第三个字母来代替(注意字母表是循环的,即认为紧随Z后的是字母A)。
A | B | C | D | E | F | G | H | I | J | K | L | M | N | O | P | Q | R | S | T | U | V | W | X | Y | Z |
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
对于每个明文字母p,代替成密文字母C,加密算法可以表示为:C=E(3,p) = (p+3)mod(26)
其中的位移量3可以是任意整数k,这样就得到了一般的Caesar算法:C=E(k,p)=(p+k)mod(26)
这里K的取值范围从1到25,解密算法是:p=D(k,C)=(C-k)mod(26)
package algorithm;
/**
* @author GDUYT
* <br>凯撒密码
* */
public class Caesar {
/**
* 加密方法
* <br>String plaintext: 明文字符串
* <br>int offset: 偏移量,取值为任意整数
* */
public String encrypt(String plaintext,int offset) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < plaintext.length(); i++) {
int c = plaintext.charAt(i);
if (c >= 'A' && c <= 'Z') { //如果字符是大写字母
//对26取余的结果在[-25,25]之间,对取余的结果+26然后再对26取余,结果就落在了[0,25]之间
c = (((c - 'A' + offset) % 26) + 26) % 26 + 'A';
} else if (c >= 'a' && c <= 'z') { //如果字符是小写字母
c = (((c - 'a' + offset) % 26) + 26) % 26 + 'a';
}
char d = (char)c;
sb.append(d);
}
return sb.toString();
}
/**
* 解密方法
* <br>ciphertext: 密文字符串
* <br>offset:偏移量,取值为任意整数
* */
public String decrypt(String ciphertext,int offset) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < ciphertext.length(); i++) {
int c = ciphertext.charAt(i);
if (c >= 'A' && c <= 'Z') { //如果字符是大写字母
c = (((c - 'A' - offset) % 26) + 26) % 26 + 'A';
} else if (c >= 'a' && c <= 'z') { //如果字符是小写字母
c = (((c - 'a' - offset) % 26) + 26) % 26 + 'a';
}
char d = (char)c;
sb.append(d);
}
return sb.toString();
}
}
//部分测试代码
public static void main(String[] args) {
try {
String plaintext = FileOperate.ReadIn("D:/programming/java/mec/java_ee/code/EncryptionAlgorithm/src/test/plaintext.txt");
System.out.println("明文:" + plaintext);
Caesar caesar = new Caesar();
String ciphertext = caesar.encrypt(plaintext,-1000);
System.out.println("加密:" + ciphertext);
String plaintext1 = caesar.decrypt(ciphertext, -1000);
System.out.println("解密:" + plaintext1);
} catch (IOException e) {
e.printStackTrace();
}
}
//测试结果:
明文:abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
加密:opqrstuvwxyzabcdefghijklmnOPQRSTUVWXYZABCDEFGHIJKLMN
解密:abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
验证:(1).J在字母表中对应10,10 + (-1000) = -990,-990 % 26 = -2 ,-2 + 26 = 24,24对应字母表中的X,故加密正确。
(2).X在字母表中对应24,24 - (-1000) = 1024,1024 % 26 = 10,10对应字母表中的J,解密正确。
2.仿射Caesar密码
即为单表加密的一种替换密码,也是Caesar密码的一种推广。具有如下定义:对于每一个明文p,其密文用c代表,则
加密函数: c = E ([a, b] , p) = (ap + b) mod m (其中m为字母的数目,在仿射Caesar密码中取26,a与m互质)
对加密算法的一个基本要求是算法是单射的,即如果pq,则E(k,p) E(k,q)。否则就会因为有很多的明文映射成相同的密文而不能解密。若不对其中的参数a、b做出限制,则部分加密过程无法做到一对一的映射。例如,当a = 2,b = 3,时,有E([a, b] ,0) = E([a, b], 13) = 3。
所以首先对与b的取值,若要E(k,p) E(k,q),则ap + b (aq + b) mod 26不成立,则ap - aq 0 mod 26不成立,此时b已经消去,因此可以得到对于b的取值没有限制。
然后对于a的取值,若要使E(k,p) = E(k,q) (0 p p 26)成立,当且仅当26整除a(p - q),因为|p-q| < 26, 所以p-q不可能是26的倍数,但有可能是2或13的倍数,因此a不能为2或13的倍数,即要求a与26互质。
解密函数:对于密文c,其明文p = D([a, b], c) = (c - b) mod m (此处满足1 a mod m)
a之乘法逆元素仅存在于a与m互质条件下。 由此,没有a的限制,可能无法解密。 易知解密方程逆于加密方程:
D(E(x)) = (E(x) - b) mod m = (((ax + b) mod m) - b) mod m = (ax + b - b) mod m = ax mod m
package algorithm;
/**
* @author GDUYT
* <br>仿射密码
* */
public class Affine {
/**
* 加密方法
* <br>String plaintext: 明文字符串
* <br>int a: 乘数a(a与m互质,m为字母元素种类个数,在仿射Caesar密码中m取26)
* <br>int b: 位移b,取值为任意整数
* */
public String encrypt(String plaintext, int a, int b) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < plaintext.length(); i++) {
int c = plaintext.charAt(i);
if (c >= 'A' && c <= 'Z') { //如果字符是大写字母
//对26取余的结果在[-25,25]之间,对取余的结果+26然后再对26取余,结果就落在了[0,25]之间
c = ((a * (c - 'A') + b) % 26 + 26) % 26 + 'A';
} else if (c >= 'a' && c <= 'z') { //如果字符是小写字母
c = ((a * (c - 'a') + b) % 26 + 26) % 26 + 'a';
}
char d = (char)c;
sb.append(d);
}
return sb.toString();
}
/**
* 解密方法
* <br>String plaintext: 密文字符串
* <br>int a: 乘数a(a与m互质,m为字母元素种类个数,在仿射Caesar密码中m取26)
* <br>int b: 位移b,取值可为任意整数
* */
public String decrypt(String ciphertext, int a, int b) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < ciphertext.length(); i++) {
int c = ciphertext.charAt(i);
int e = c;
if (c >= 'A' && c <= 'Z') {
c = c - 'A' - b;
} else if (c >= 'a' && c <= 'z') {
c = c - 'a' - b;
}
while (c % a != 0) { //得到可以被a整除的c值
c += 26;
}
//对26取余的结果在[-25,25]之间,对取余的结果+26然后再对26取余,结果就落在了[0,25]之间
c = ((c / a) % 26 + 26) % 26;
//将最终得到的数字编号对应到字母表中的字母ascii值
c += (e > 96) ? 'a' : 'A';
char d = (char)c;
sb.append(d);
}
return sb.toString();
}
}
//部分测试代码:
public static void main(String[] args) throws IOException {
String plaintext = FileOperate.ReadIn
("D:/programming/java/mec/java_ee/code/EncryptionAlgorithm/src/test/plaintext.txt");
System.out.println("明文:" + plaintext);
Affine affine = new Affine();
String ciphertext = affine.encrypt(plaintext, 7, 64);
System.out.println("加密:" + ciphertext);
String plaintext1 = affine.decrypt(ciphertext, 7, -64);
System.out.println("解密:" + plaintext1);
}
//输出结果:
明文:abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
加密:mtahovcjqxelszgnubipwdkryfMTAHOVCJQXELSZGNUBIPWDKRYF
解密:abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
验证:(1)、字母e对应4, 4 * 7 + (64) = 92,92 % 26 = 14,14对应字母表中的o,故加密正确。
(2)、字母o对应14,14 - 64 = -50,-50 + 26 = -24,-24 + 26 = 2,2 + 26 = 28,28可以被7整除,28 / 7 = 4,4对应字母e,所以解密正确。