1. 程式人生 > >CBC加密模式本身不能抵禦重放攻擊

CBC加密模式本身不能抵禦重放攻擊

  一些文章書籍中講到,ECB、CFB加密模式不能抵禦重放攻擊,舉的例子是:用舊報文替換部分新報文達到欺騙接收者的目的。言外之意,似乎CBC模式可以抵禦這種攻擊。實際情況是不是這樣呢?網上一通翻騰竟然無果,那就自己試試吧。雖然小白,不明白大佬的高明,但單從示例類同性上確實要弄清楚為什麼。     首先從理論上分析,CBC加密模式的原理是,對每一個明文塊,先用前一個密文塊與之異或,爾後再用密碼演算法加密,就得到新的密文塊,以此類推,初始的密文塊由初始向量提供。計算公式為:C[i]=E(K, P[i]^C[i-1]),這裡C代表密文、P代表明文、K代表金鑰、E代表密碼演算法、i代表第幾個塊;解密公式是反過來,即先經過密碼演算法、再與前一密文塊異或。示意圖見下,引自維基百科。 CBC加密模式示意圖

CBC解密模式示意圖   可以看出每一個明密對(P,C)只與3個要素關聯,一是密碼演算法E,二是金鑰K,三是前一密文塊C。E是不變的,假設K也是不變的,攻擊者又掌握著多份截獲的C,則P一定能計算出來,也就是攻擊者用舊密文替換新密文中的連續塊,就可以使合法接收者得到混雜了過時資訊的“新”資訊。這就是重放攻擊,與一些書中描述的CFB重放攻擊一模一樣。   下面是CFB解密模式示意圖,引自維基百科。 CFB解密模式示意圖   程式驗證。以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、序號、時間戳、簽名、認證等要素,單純依靠這種通用模式是不行的。