定义及原理
仿射密码是移位密码的一个推广,其加密过程不仅包含移位操做,而且使用了乘法运算,与移位密码相同,仿射密码的明文空间M和密文空间C均为$Z_{26}$,因此,在使用使用仿射密码体制对英文消息进行加密之前,需要在26个英文字母与$Z_{26}$的元素之间建立一一对应的关系,其实也就相当于一种单表替换。
字母表的每个字母相应的值使用一个简单的数学函数对应一个数值,再把对应数值转换成字母。
eg:
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
仿射密码体制
令 $M = C = Z_{26}$ , 密钥空间 K = { (k1,k2)∈ Z26 X Z26 :gcd(k1,26) = 1}。对任意的密钥key = (k1,k2) ∈ K,x∈ M,y∈C,定义 :
$E(x)= (k_1 *x+k_2) \quad mod \quad26$
$D(x) = k_1^{-1}(y-k_2)\quad mod \quad26$
其中,$k_1^{-1}$表示在$Z_{26}$中的乘法逆;$gcd(k_1,26) = 1$表示$k_1$与26互素。
python代码实现
def encrypt(plaintext, a, b):
ciphertext = ""
for char in plaintext:
if char.isalpha():
if char.isupper():
encrypted_char = chr((a * (ord(char) - 65) + b) % 26 + 65)
else:
encrypted_char = chr((a * (ord(char) - 97) + b) % 26 + 97)
else:
encrypted_char = char
ciphertext += encrypted_char
return ciphertext
def decrypt(ciphertext, a, b):
plaintext = ""
mod_inverse_a = 0
for i in range(26):
if (a * i) % 26 == 1:
mod_inverse_a = i
break
for char in ciphertext:
if char.isalpha():
if char.isupper():
decrypted_char = chr((mod_inverse_a * (ord(char) - 65 - b)) % 26 + 65)
else:
decrypted_char = chr((mod_inverse_a * (ord(char) - 97 - b)) % 26 + 97)
else:
decrypted_char = char
plaintext += decrypted_char
return plaintext
# 输入明文、加密参数a和b
plaintext = input("请输入明文: ")
print("加密参数的可能取值:3,5,7,9,11,15,17,19,21,23,25")
a = int(input("请输入加密参数a: "))
b = int(input("请输入加密参数b: "))
# 加密
encrypted_text = encrypt(plaintext, a, b)
print("加密后的密文: " + encrypted_text)
# 解密
decrypted_text = decrypt(encrypted_text, a, b)
print("解密后的明文: " + decrypted_text)
CTF例题
题目代码:
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"
解题脚本
首先判断加密的类型
c = (ord(a) * c + ord(b)) % 251
由此可得这应该是一个模251的仿射密码,为什么会模251?,他这里用的表不仅仅是26个英文字母了,用的的ASCII码表0~255,选一个接近255的质数比较安全
但是现在我们不知道两个密钥 a,b,所以首先我们要求出a,b,然后再利用仿射密码解密来求出flag
import gmpy2
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] #提取已知密文
#原来的加密公式
#y1 = (a * x1 + b) (mod 26)
#y2 = (a * x2 + b) (mod 26)
#故有
#y1 - y2 = a*(x1-x2)(mod 251)
#想要求a
#即为
#plaindelta = x1-x2
#cipherdalta = y1-y2
#cipherdalte = a * plaindelta(mod 251) !!取模只是让最后的结果在0~251范围之间!!
#计算公式中的a,引入初等数论里面的内容
#ci = a * p % 251
#ci / p = a % 251
#在初等数论中 除相当于乘逆元
#故 ci * invert(p,251)= a (% 251)
#所以%251只是一个对于整体结果的一个限制,在公式推到的时候可以忽略存在
a = gmpy2.invert(plaindelta, 251) * cipherdalte % 251 #数据全部放在0~251之间
#通过a计算b
b = (encrypted[0] - a * ord(flag[0])) % 251 #数据全部放在0~251之间
a_inv = gmpy2.invert(a, 251)
result = ""
for c in encrypted:
result += chr((c - b) * a_inv % 251)
print(result)
#求得flag
#TWCTF{Faster_Than_Shinkansen!}