仿射密碼是一種替換密碼,利用加密函數一個字母對一個字母的加密。
加密函數:E(x) = (ax + b) (mod m),其中
- a和m互質
- m是字母的數量
解密函數:D(x) = a-1(x - b) (mod m),其中a-1是a在Zm群的乘法逆元。
仿射密碼 為單表加密的一種,字母系統中所有字母都藉一簡單數學方程加密,對應至數值,或轉回字母。 其仍有所有替代密碼之弱處。所有字母皆借由方程E(x) = (ax + b) (mod m)加密,b 為移動大小。
加密與解密
加密
以加密函數E(x) = (5x + 8) (mod 26)為例,以字母表26個字母作為編碼系統
明文 | A | F | F | I | N | E | C | I | P | H | E | R |
---|---|---|---|---|---|---|---|---|---|---|---|---|
x | 0 | 5 | 5 | 8 | 13 | 4 | 2 | 8 | 15 | 7 | 4 | 17 |
y = 5x+8 |
8 | 33 | 33 | 48 | 73 | 28 | 18 | 48 | 83 | 43 | 28 | 93 |
y mod 26 |
8 | 7 | 7 | 22 | 21 | 2 | 18 | 22 | 5 | 17 | 2 | 15 |
密文 | I | H | H | W | V | C | S | W | F | R | C | P |
對應的加密結果為:IHHWVCSWFRCP
解密
在已知a = 5,m = 26的情況下,我們需要求a關於模m的逆元,得到a-1 = 21
因此解密函數為:D(x) = 21(x - 8) (mod 26)
密文 | I | H | H | W | V | C | S | W | F | R | C | P |
---|---|---|---|---|---|---|---|---|---|---|---|---|
y |
8 | 7 | 7 | 22 | 21 | 2 | 18 | 22 | 5 | 17 | 2 | 15 |
x = 21(y−8) |
0 | -21 | -21 | 294 | 273 | -126 | 210 | 294 | -63 | 189 | -126 | 147 |
x mod 26 |
0 | 5 | 5 | 8 | 13 | 4 | 2 | 8 | 15 | 7 | 4 | 17 |
明文 | A | F | F | I | N | E | C | I | P | H | E | R |
腳本
使用字母表加密解密的腳本:
# -*- coding:utf-8 -*- import string letters = string.ascii_letters def encode(plaintext, a, b): encode_str = '' for s in plaintext: if s in letters: n = letters.find(s) % 26 y = (a * n + b) % 26 if s.isupper(): y = y + 26 encode_str += letters[y] else: encode_str += s return encode_str def ext_euclid(a, m): if m == 0: return 1, 0 else: x, y = ext_euclid(m, a % m) x, y = y, (x - (a // m) * y) return x,y def decode(encodes, a, b): decode_str = '' x = ext_euclid(a,26) a = x[0] if a < 0: a = a + 26 for s in encodes: if s in letters: n = letters.find(s) % 26 y = a * (n - b) % 26 if s.isupper(): y += 26 decode_str += letters[y] else: decode_str += s return decode_str if __name__ == '__main__': plaintext = 'AFFINE CIPHER' a = 5 b = 8 s = encode(plaintext,a,b) print ("加密:"+ s) d = decode(s, a, b) print ("解密:" + d)
破解
首先,我們可以看到的是,仿射密碼對於任意兩個不同的字母,其最后得到的密文必然不一樣,所以其也具有最通用的特點。當密文長度足夠長時,我們可以使用頻率分析的方法來解決。
其次,我們可以考慮如何攻擊該密碼。可以看出當a=1時,仿射加密是凱撒加密。而一般來說,我們利用仿射密碼時,其字符集都用的是字母表,一般只有 26 個字母,而不大於 26 的與 26 互素(1,3,5,7,9,11,15,17,19,21,23,25)的個數一共有
Φ(26) = Φ(2) x Φ(13) = 12
算上b的偏移可能,一共有可能的密鑰空間大小也就是
12 x 26 = 312
一般來說,對於該種密碼,我們至少得是在已知部分明文的情況下才可以攻擊。下面進行簡單的分析。
這種密碼由兩種參數來控制,如果我們知道其中任意一個參數,那我們便可以很容易地快速枚舉另外一個參數得到答案。
但是,假設我們已經知道采用的字母集,這里假設為 26 個字母,我們還有另外一種解密方式,我們只需要知道兩個加密后的字母 y1,y2即可進行解密。那么我們還可以知道
y1 = (ax1 + b) (mod 26)
y2 = (ax2 + b) (mod 26)
兩式相減得
y1 - y2 = a(x1 - x2) (mod 26)
這里 y1,y2已知,如果我們知道密文對應的兩個不一樣的字符 x1與x2 ,那么我們就可以很容易得到 a ,進而就可以得到b 了。
例子
TWCTF 2016的super_express
import sys key = '****CENSORED***************' flag = 'TWCTF{*******CENSORED********}' if len(key) % 2 == 1: print("Key Length Error") sys.exit(1) n = len(key) / 2 encrypted = '' for c in flag: c = ord(c) for a, b in zip(key[0:n], key[n:2*n]): c = (ord(a) * c + ord(b)) % 251 encrypted += '%02x' % c print encrypted
加密得到:805eed80cbbccb94c36413275780ec94a857dfec8da8ca94a8c313a8ccf9
對於 flag 中的每個字母都加密了 n 次,仔細分析,我們可以發現
c1 = a1c + b1
c2 = a2c1 + b2
= a1a2c + a2b1 + b2
= kc + d
根據第二行的推導,我們可以得到其實 cn 也是這樣的形式,可以看成 cn=xc+y ,並且,我們可以知道的是,key 是始終不變化的,所以說,其實這個就是仿射密碼。
此外,題目中還給出了密文以及部分部分密文對應的明文,那么我們就很容易利用已知明文攻擊的方法來攻擊了,利用代碼如下
# -*- coding:utf-8 -*- import gmpy key = '****CENSORED***************' flag = 'TWCTF{*******CENSORED********}' data = "805eed80cbbccb94c36413275780ec94a857dfec8da8ca94a8c313a8ccf9" encrypted = [int(data[i:i + 2], 16) for i in range(0, len(data), 2)] plaindelta = ord(flag[1]) - ord(flag[0]) cipherdalte = encrypted[1] - encrypted[0] a = gmpy.invert(plaindelta, 251) * cipherdalte % 251 b = (encrypted[0] - a * ord(flag[0])) % 251 a_inv = gmpy.invert(a, 251) result = "" for c in encrypted: result += chr((c - b) * a_inv % 251) print result
gmpy安裝方式:點擊進入
在線加密解密網站:https://crypto.interactive-maths.com/affine-cipher.html
參考
https://zh.wikipedia.org/wiki/%E4%BB%BF%E5%B0%84%E5%AF%86%E7%A2%BC
https://en.wikipedia.org/wiki/Affine_cipher
https://ctf-wiki.github.io/ctf-wiki/crypto/classical/monoalphabetic-zh/