图解密码技术-对CFB模式的攻击

CFB模式简介

CFB模式全称是(Cipher FeedBack)密文反馈模式。加密方式是前一个密文分组会被送回到密码算法的输入端。简单理解就是,前一个密文分组在此加密后会当作下一次与明文异或运算的一部分,依次类推。如图:

image-20230810223911494

CFB模式的解密

因为CFB模式的加密利用了前一个密文分组,则解密的核心思路就是利用前一个明文分组的加密结果和当前密文进行异或运算得到当前明文。

文字步骤如下:

  1. 解密端获得密文块C1、C2、C3…以及一个初始向量IV。
  2. 将IV进行加密,获得第一个密钥块K1。(使用的密钥和加密算法与加密端相同)
  3. 将密钥块K1与密文块C1进行异或,得到明文块P1。
  4. 将刚刚解密得到的明文块P1再次进行加密,生成下一个密钥块K2。
  5. 将K2与下一个密文块C2进行异或,得到明文块P2。
  6. 依此类推,重复步骤4和5,直到所有密文全部解密。

图示:

image-20230814225641064

CFB模式的攻击

书中提到了一种攻击方式是重放攻击。(replay attack)

加入小美发送了一条信息,这条信息由4个密文组成。主动攻击者小明将消息的后3个密文分组保存下来,等第二天小美使用相同密钥再一次发送新消息的时候(新消息由4个新的密文组成)。小明就可以将昨天保存的密文分组进行替换。那么在解密的时候就会得到错误的信息。在真实的网络环境中,服务器与服务器之间的对话如果被攻击者利用进行重新发送或者延迟发送数据就会达到欺骗用户的目的。

Java代码模拟重放攻击

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
package com.yt.mvcframework.v2.CFBDemo;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import java.security.Key;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

public class ReplayAttackExample {

private static class Server {
private Cipher cipher;
private SecretKey key;
private Map<Long, byte[]> seenMessages = new HashMap<>();

public Server(SecretKey key, IvParameterSpec iv) throws Exception {
this.key = key;
this.cipher = Cipher.getInstance("AES/CFB/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, key, iv);
}

public void receiveMessage(long timestamp, byte[] ciphertext) throws Exception {
if (seenMessages.containsKey(timestamp) &&
Arrays.equals(seenMessages.get(timestamp), ciphertext)) {
System.out.println("Replay attack detected!");
return;
}

byte[] plaintext = cipher.doFinal(ciphertext);
System.out.println("Received message: " + new String(plaintext));

seenMessages.put(timestamp, ciphertext);
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(cipher.getParameters().getParameterSpec(IvParameterSpec.class).getIV()));
}
}

private static class Client {
private Cipher cipher;
private SecretKey key;

public Client(SecretKey key, IvParameterSpec iv) throws Exception {
this.key = key;
this.cipher = Cipher.getInstance("AES/CFB/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
}

public byte[] sendMessage(String message) throws Exception {
byte[] ciphertext = cipher.doFinal(message.getBytes());
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(cipher.getParameters().getParameterSpec(IvParameterSpec.class).getIV()));
return ciphertext;
}
}

public static void main(String[] args) throws Exception {
KeyGenerator kg = KeyGenerator.getInstance("AES");
SecretKey key = kg.generateKey();

SecureRandom random = new SecureRandom();
IvParameterSpec iv = new IvParameterSpec(random.generateSeed(16));

Server server = new Server(key, iv);
Client client = new Client(key, iv);

long timestamp = System.currentTimeMillis();
byte[] ciphertext = client.sendMessage("Hello world");

server.receiveMessage(timestamp, ciphertext);
server.receiveMessage(timestamp, ciphertext); // Attempt replay attack
}
}


在上述代码模拟中,客户端向服务器发送了一个加密的消息和一个时间戳,服务器接收到消息后解密并保存。然后,客户端再次发送相同的加密消息和时间戳,试图欺骗服务器认为这是一个新的、合法的消息。

服务器使用了一个简单的策略来防止重放攻击:它保存了所有接收到的消息和对应的时间戳。当接收到一个新的消息时,服务器会检查它是否已经接收过一个具有相同时间戳和加密消息的消息。如果是,那么服务器就认为这是一个重放攻击,并拒绝处理这个消息。

这个模拟的关键在于,服务器能够识别并拒绝重放的消息,即使这个消息是合法的并且正确加密的。这是因为在一个安全的系统中,每个消息都应该是唯一的,即使是两个完全相同的消息也应该有不同的时间戳或者序列号,以防止重放攻击。

碰撞攻击

CFB模式的加密存在一个缺点,那就是相同的明文会产生相同的密文。攻击者可以构造特殊的明文,使得产生大量相同的密文块,从而获取明文信息。

Java代码模拟碰撞攻击

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
26
27
28
29
30
31
32
33
34
35
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;

public class CFBAttack {

public static void main(String[] args) throws Exception {

// 生成密钥
KeyGenerator kg = KeyGenerator.getInstance("AES");
SecretKey key = kg.generateKey();

// 初始化IV
IvParameterSpec iv = new IvParameterSpec(new byte[16]);

// 设置加密模式为CFB,并获得Cipher实例
Cipher cipher = Cipher.getInstance("AES/CFB/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key, iv);

// 构造明文block1和block2完全相同
byte[] block1 = "HelloWorld".getBytes();
byte[] block2 = "HelloWorld".getBytes();

// 加密两块明文
byte[] cipherBlock1 = cipher.doFinal(block1);
byte[] cipherBlock2 = cipher.doFinal(block2);

// 两个密文块由于明文相同也完全相同
// 这可被攻击者利用来获取明文信息
if (java.util.Arrays.equals(cipherBlock1, cipherBlock2)) {
System.out.println("Collision attack succeeded!");
}
}
}

这个简单的例子构造了相同的明文块,导致CFB模式下产生了相同的密文,模拟了碰撞攻击。实际上攻击者可以通过观察大量密文,找到碰撞的密文块,从而对明文信息进行推断。