CBC加密模式本身不能抵御重放攻击

  一些文章书籍中讲到,ECB、CFB加密模式不能抵御重放攻击,举的例子是:用旧报文替换部分新报文达到欺骗接收者的目的。言外之意,似乎CBC模式可以抵御这种攻击。实际情况是不是这样呢?网上一通翻腾竟然无果,那就自己试试吧。虽然小白,不明白大佬的高明,但单从示例类同性上确实要弄清楚为什么。  
  首先从理论上分析,CBC加密模式的原理是,对每一个明文块,先用前一个密文块与之异或,尔后再用密码算法加密,就得到新的密文块,以此类推,初始的密文块由初始向量提供。计算公式为:C[i]=E(K, P[i]^C[i-1]),这里C代表密文、P代表明文、K代表密钥、E代表密码算法、i代表第几个块;解密公式是反过来,即先经过密码算法、再与前一密文块异或。示意图见下,引自维基百科。
CBC加密模式本身不能抵御重放攻击_第1张图片CBC加密模式本身不能抵御重放攻击_第2张图片
  可以看出每一个明密对(P,C)只与3个要素关联,一是密码算法E,二是密钥K,三是前一密文块C。E是不变的,假设K也是不变的,攻击者又掌握着多份截获的C,则P一定能计算出来,也就是攻击者用旧密文替换新密文中的连续块,就可以使合法接收者得到混杂了过时信息的“新”信息。这就是重放攻击,与一些书中描述的CFB重放攻击一模一样。
  下面是CFB解密模式示意图,引自维基百科。
CBC加密模式本身不能抵御重放攻击_第3张图片
  程序验证。以DES密码为例,采用CBC加密模式,密钥保持不变,先对一段明文加密,再对另一段明文加密,然后将两个密文重新组合成新密文,再解密,得到了明文1和明文2的混搭。

package main
import (
	"fmt"
	"crypto/des"
	"crypto/cipher"
)

func main() {
	// new DES, key immutable
	b, err := des.NewCipher([]byte("12345678"))
	if err != nil {
		fmt.Println(err)
	}

	// plain/cipher text 1
	be := cipher.NewCBCEncrypter(b, []byte("11111111"))//iv1
	bd := cipher.NewCBCDecrypter(b, []byte("11111111"))
	p1 := []byte("12345678abcdefghABCDEFGH87654321")//plaintext
	var c1 = make([]byte, len(p1))//ciphertext
	var r1 = make([]byte, len(p1))//decrypttext
	be.CryptBlocks(c1, p1)
	bd.CryptBlocks(r1, c1)
	fmt.Printf("// nomal encrypt/decrypt 1:\n")
	fmt.Printf("plaintext1 : %s\n", p1)
	fmt.Printf("ciphertext1: % 2x\n", c1)
	fmt.Printf("decryptext1: %s\n\n", r1)

	// plain/cipher text 2
	be = cipher.NewCBCEncrypter(b, []byte("22222222"))//iv2
	bd = cipher.NewCBCDecrypter(b, []byte("22222222"))
	p2 := []byte("ABCDEFGH8765432112345678abcdefgh")
	var c2 = make([]byte, len(p2))
	var r2 = make([]byte, len(p2))
	be.CryptBlocks(c2, p2)
	bd.CryptBlocks(r2, c2)
	fmt.Printf("// nomal encrypt/decrypt 2:\n")
	fmt.Printf("plaintext2 : %s\n", p2)
	fmt.Printf("ciphertext2: % 2x\n", c2)
	fmt.Printf("decryptext2: %s\n\n", r2)

	// replay attack with fault cipher(c2[0:8]+c1[8:])
	bd = cipher.NewCBCDecrypter(b, []byte("22222222"))
	c := append(c2[:8], c1[8:]...)
	var r = make([]byte, len(p1))
	bd.CryptBlocks(r, c)
	fmt.Printf("// replay attack with cipher2[:8] + cipher1[8:]:\n")
	fmt.Printf("faultcipher: % 2x\n", c)
	fmt.Printf("decryptext : %s\n\n", r)
}

  运行结果如下,最后解密乱码部分对应的正好是重放的第一块密文:

// nomal encrypt/decrypt 1:
plaintext1 : 12345678abcdefghABCDEFGH87654321
ciphertext1: 6e 8b 79 29 82 6f ae de af ea b3 18 51 0e 83 25 d6 11 19 34 d4 82 f4 0d ed 0e 1f fc a5 da cc 6e
decryptext1: 12345678abcdefghABCDEFGH87654321

// nomal encrypt/decrypt 2:
plaintext2 : ABCDEFGH8765432112345678abcdefgh
ciphertext2: 1b 6d eb 8b f7 cd ce bd 48 eb 3b 5c 4b fe cb 66 9b d1 7b f0 c1 aa 77 04 22 fa 95 c3 c5 a2 4e f7
decryptext2: ABCDEFGH8765432112345678abcdefgh

// replay attack with cipher2[:8] + cipher1[8:]:
faultcipher: 1b 6d eb 8b f7 cd ce bd af ea b3 18 51 0e 83 25 d6 11 19 34 d4 82 f4 0d ed 0e 1f fc a5 da cc 6e
decryptext : ABCDEFGH����ABCDEFGH87654321

  如上说明,防止这种“混搭”式的重放攻击,还需要从应用协议的角度引入nonce、序号、时间戳、签名、认证等要素,单纯依靠这种通用模式是不行的。

你可能感兴趣的:(sec)