仿射密码——笔记1

仿射密码——笔记1

目录

  • 仿射密码——笔记1
    • 原理
    • 无密钥破译分析
    • 代码

原理

  • 是代换密码的一种特殊情形
  • 对每一个明文字符x,其加密(代换)后为 e ( x ) = ( a x + b ) m o d   26 e(x)=(ax+b)mod\,26 e(x)=(ax+b)mod26
    即 { 加 密 函 数     e ( x ) = ( a x + b ) m o d   26 解 密 函 数     d ( y ) = a − 1 ( y − b ) m o d   26 即 \begin{cases} 加密函数\,\,\,e(x)=(ax+b)&mod\,26\\ 解密函数\,\,\,d(y)=a^{-1}(y-b)&mod\,26 \end{cases} {e(x)=(ax+b)d(y)=a1(yb)mod26mod26
  • 需要注意的是:当且仅当a,26互质,即 g c d ( a , 26 ) = 1 gcd(a,26)=1 gcd(a,26)=1时,加密函数有效,否则 e ( x ) e(x) e(x)不是一个单射函数,a的逆元不存在

无密钥破译分析

  • 需要把密钥 ( a , b ) (a,b) (a,b)找到,那么如何才能找到呢?
    • 我们知道,仿射加密是代换密码的一种,也就是说明文和密文一一对应
    • 所以如果我们可以找到2组加密前后的结果,或许可以构造方程把 ( a , b ) (a,b) (a,b)求解出来
      • 即如果有 { e ( m 1 ) = c 1 e ( m 2 ) = c 2 ⇒ { m 1 a + b = c 1 m 2 a + b = c 2 m o d   26 \begin{cases} e(m_1)=c_1\\ e(m_2)=c_2 \end{cases} \Rightarrow \begin{cases} m_1a+b=c_1\\ m_2a+b=c_2 \end{cases}mod\,26 {e(m1)=c1e(m2)=c2{m1a+b=c1m2a+b=c2mod26
        (其中 m 1 , c 1 , m 2 , c 2 m_1,c_1,m_2,c_2 m1,c1,m2,c2均已知),我们或许可以将 ( a , b ) (a,b) (a,b)求解出来
      • 求解结果为 { a = ( m 1 − m 2 ) − 1 ( c 1 − c 2 ) b = c 1 − m 1 a m o d   26 \begin{cases} a={(m_1-m_2)}^{-1}(c_1-c_2)\\ b=c_1-m_1a \end{cases}mod\, 26 {a=(m1m2)1(c1c2)b=c1m1amod26
      • 需要注意的是 ( m 1 − m 2 ) (m_1-m_2) (m1m2)不一定存在逆元,所以即便我们找到了两组正确的加密前后结果,也不一定可以将 ( a , b ) (a,b) (a,b)求解出来,所以需要选择多组数据计算
    • 那么接下来就是找到2组或更多加密前后结果了
      • 如果已知多组加密前后结果,可以进行已知明文攻击
      • 如果只知道密文,则只能进行唯密文攻击
        • 由于英文各字母出现频率不同,可以对密文中各字母频数进行统计,然后进行猜测
          • 如密文中最大频数字母为R,而我们知道英文中出现频率最大的字母是E,则我们可以猜测R为E加密后的结果
    • 找到多组结果后对 ( a , b ) (a,b) (a,b)进行求解
    • 对密文解密并判断是否有意义
      • 因为在唯密文攻击下根据选取的数据不同,可能求解出多组 ( a , b ) (a,b) (a,b)
      • 这个时候就需要人为去判断解密后的结果是否有意义

代码

import gmpy2
# 保证输入都是字母

class affineCipher:
    def __init__(self, msg):
        self.msg=msg.lower()
        self.freq={}
        for i in self.msg:
            try:
                self.freq[i]+=1
            except KeyError:
                self.freq[i]=1
        self.freq=dict(sorted(self.freq.items(),key=lambda items:items[1],reverse=True))
    # e,d都是对单个数字进行转换
    def e(self,a,b,x):
        return (a*x+b)%26
    def d(self,a,b,y):
        assert int(gmpy2.gcd(a,26))==1
        ainv=int(gmpy2.invert(a,26))
        return (ainv*(y-b))%26
    # 将msg根据a,b进行加、解密
    def encode(self,a,b):
        res=""
        for i in self.msg:
            x=ord(i)-ord("a")
            res+=chr(self.e(a,b,x)+ord("a"))
            # res+=chr((a*x+b)%26+ord("a"))
        return res
    def decode(self,a,b):
        res=""
        for i in self.msg:
            y=ord(i)-ord("a")
            res+=chr(self.d(a,b,y)+ord("a"))
        return res
    # 更新msg
    def update(self,msg):
        self.msg=msg.lower()
    # 输出msg中各字符出现频数
    def showDict(self):
        print(self.freq)
    # 手动选择加解密前后结果,进行破译
    def tryAB(self):
        while(True):
            print("频数统计:")
            self.showDict()
            str1=input("猜测密文字母1:")
            str2=input("猜测明文字母1:")
            str3=input("猜测密文字母2:")
            str4=input("猜测明文字母2:")
            c1=ord(str1[0].lower())-ord("a")
            m1=ord(str2[0].lower())-ord("a")
            c2=ord(str3[0].lower())-ord("a")
            m2=ord(str4[0].lower())-ord("a")
            try:
                a=int(gmpy2.invert(m1-m2,26))*(c1-c2)%26
                b=(c1-m1*a)%26
                print("此组a,b解密为:"+self.decode(a,b),end='\n\n')
            except:
                print("此组a,b无效",end='\n\n')
    # 暴力破解,每次输出十组可能破译结果
    def vioCrack(self):
        cnt=0
        fq="etaoinshrdlucmfwypvbgkjqxz"
        len_fq=len(fq)
        lst=list(self.freq.keys())
        len_key=len(lst)

        for k in range(len_key):
            for t in range(k+1,len_key):
                c1,c2=[ord(each)-ord("a") for each in [lst[k],lst[t]]]
        # c1,c2=[ord(i)-ord("a") for i in list(self.freq.keys())[:2]] 
                for i in range(len_fq):
                    for j in range (i+1,len_fq):
                        m1,m2=[ord(each)-ord("a") for each in [fq[i],fq[j]]]
                        # print("(m1,m2)=(%d,%d):"%(m1,m2))
                        # print(fq[i]+fq[j])
                        try:
                            a=int(gmpy2.invert(m1-m2,26))*(c1-c2)%26
                            b=(c1-m1*a)%26
                            print("(a,b)=(%d,%d):"%(a,b)+self.decode(a,b),end='\n\n')
                            cnt+=1
                            if cnt%10==0:
                                s=input("输入q退出。。。")
                                if ord(s[0]) in [ord('q'),ord("Q")]:
                                    return
                        except:
                            pass

if __name__=="__main__":
    # msg="FMXVEDKaPHFERBNDKRXRSREFMORUDSDKDVSHVUFEDKaPRKDLYEVLRHHRH"
    msg="KQEREJEBCPPCJCRKIEACUZBKRVPKRBCIBQCARBJCVFCUPKRIOFKPACUZQEPBKRXPEIIEABDKPBCPFCDCCAFIEABDKPBCPFEQPKAZBKRHAIBKAPCCIBURCCDKDCCJCIDFUIXPAFFERBICZDFKABICBBENEFCUPJCVKABPCYDCCDPKBCOCPERKIVKSCPICBRKIJPKABI"
    t=affineCipher(msg)
    t.vioCrack()
    # t.tryAB()

你可能感兴趣的:(密码学,笔记)