仿射密码


定义及原理

仿射密码是移位密码的一个推广,其加密过程不仅包含移位操做,而且使用了乘法运算,与移位密码相同,仿射密码的明文空间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!}

文章作者: f14g
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 f14g !
评论
  目录