密码学学习笔记 2.传统加密技术——代替技术(一)

博客介绍了加密技术的替代和置换方法,重点讲解了Caesar密码、单表代替密码和Playfair密码。分析了Caesar密码的可靠性及破解可能,探讨了单表代替密码的改进与破解方式,还介绍了Playfair密码的加密过程、优势与劣势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

所有加密技术都使用两种方法,替代和置换;替代技术就是将明文字母替换成为其他字母、数字或符号。


Caesar密码

规则:将每个字母替换成为字母表中对应的后面的第三个字母。

明文abcdefghijklmn
密文DEFGHIJKLMNOPQ
明文opqrstuvwxyz  
密文RSTUVWXYZABC  

举例:

明文:i'm growing like a seed, rains been falling on me.

密文:lpjurzlqjolnhdvhhgudlqvehhqidoolqjrqph

那么来看一下,怎么实现。其实很简单,这就相当于一个散列,然后把明文对应存入到密文中,而公式也很简单:

C=E(3,P)=(P+3)mod 26,mod的意思是取模运算,如果不了解可以尝试学习一下信息安全数学基础(emmmm在我们学校是这么叫这门课)。。。

当然这样看来,这个公式可以进行扩展,即把Key从3变为其他值:

加密过程:C=E(K,P)=(P+K)mod 26 

解密过程:P=D(K,C)=(C+26-K)mod 26

接下来我们分析一下它的可靠性:从这个加密过程中我们可以了解,一旦我们确定a的密文,那么其他25个密文也就都出来了,那么我们就可以直接分析这个a,设想一下你不知道k的值,但是很容易就可以发现,mod 26,这个运算把整数空间分成了26子空间,而a只能在这26个子空间中的一个,那我们假定,加密过的密文不和明文重复,那a的密文顶多有25种取法,那也就是说,这个密码,最多尝试25次就可以得到明文。

于是,我写了个python来尝试了一下(我的代码功底可以说是非常差。。。所以大家勉强看。。。)

import re
import sys

in_text=sys.argv[1:]
P_E_S=['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']

def encryption(plaintex,key):
    key=int(key)
    ciphertext=[]
    for i in plaintex:
        ciphertext.append(P_E_S[(P_E_S.index(i)+key)%26])
    return ciphertext

def decryption(ciphertext,key):
    key=int(key)
    plaintext=[]
    for i in ciphertext:
        if P_E_S.index(i)-key >= 0:
            plaintext.append(P_E_S[(P_E_S.index(i)-key)%26])
        else:
            plaintext.append(P_E_S[(P_E_S.index(i)+26-key)%26])
    return plaintext

def caesar(in_text):
    print("此加密解密只涉及26个英文字母")
    print('---------------------------------')
    choice=input("1. 加密 2. 解密 ")
    key=input('密钥: ')
    in_text=''.join(in_text)
    in_text=re.compile('[^a-zA-Z]').sub('', in_text.lower())
    if choice is '1':
        print('加密结果: '+''.join(encryption(in_text,key)))
    elif choice is '2':
        print("解密结果: "+''.join(decryption(in_text,key)))
    else:
        print("输入错误")

def guess_P(ciphertext):
    ciphertext=''.join(ciphertext)
    ciphertext=re.compile('[^a-zA-Z]').sub('', ciphertext.lower())
    for j in range(1,26):
        print("%d. "%j+''.join(decryption(ciphertext,j)))
    print("请在上述结果中挑选有用的信息")

if __name__ == '__main__':
    try:
        cho=input("1. 加解密 2. 猜明文 ")
        if cho is '1':
            caesar(in_text)
        elif cho is '2':
            guess_P(in_text)
        else:
            print("无效选项")
    except EOFError:
        exit()

 从中可以看到,还是比较容易得出结果的。但是这也并不总是这样简单,比如,如果进行加密的是一些其他信息,不是自然语言,那么可能得出的结果一点可读性都没有,那么你就有很小的概率能分析出哪个才是真正的明文了。


单表代替密码

显然Caesar是不安全的,那么我们来像一种更安全的办法。Caesar的密文一共最多有25种形式,这主要是因为abcd......的排列顺序没有改变,这样就大大缩小了密文集。那如果,我们打乱这个顺序呢?不再按照默认序列进行替换,而是按照表单替换呢?

那么a就有26种方式,b就有25种方式,c就有24种方式。。。最终的结果就是一共有 26! 种密文的形式,显然这个密文集要比Caesar形成的大的多。

我们就来设计一个表单:

明文abcdefghijklm
密文qwertyuiopasd
明文nopqrstuvwxyz
密文fghjklzxcvbnm

明文:i'm growing like a seed, rains been falling on me.

密文:odukgvofusoatqlttrkqoflwttfyqssofugfdt

这种密码的缺点在于,自然语言是有特性的。比如字母使用频率,单词使用频率,固定词组,固定句型等等。那么就有破解的方法了。

1. 使用字母使用频率来破解:

英文字母使用频率

这是通过对英文字典分析得出字母使用频率,不难看出e的使用频率非常高。然后我写了一段代码,对我们得出的密文进行了分析

import re
import sys

in_text=sys.argv[1:]
P_E_S=['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']
R_A=['q','w','e','r','t','y','u','i','o','p','a','s','d','f','g','h','j','k','l','z','x','c','v','b','n','m']

def encryption(plaintex):
    ciphertext=[]
    for i in plaintex:
        ciphertext.append(R_A[P_E_S.index(i)])
    return ciphertext

def decryption(ciphertext):
    plaintext=[]
    for i in ciphertext:
        plaintext.append(P_E_S[R_A.index(i)])
    return plaintext

def SingleSableSub(in_text):
    print("此加密解密只涉及26个英文字母")
    print('---------------------------------')
    choice=input("1. 加密 2. 解密 3. 分析 ")
    in_text=''.join(in_text)
    in_text=re.compile('[^a-zA-Z]').sub('', in_text.lower())
    if choice is '1':
        print('加密结果: '+''.join(encryption(in_text)))
    elif choice is '2':
        print("解密结果: "+''.join(decryption(in_text)))
    elif choice is '3':
        print("分析结果:")
        print(analysis(in_text))
    else:
        print("输入错误")

def analysis(ciphertext):
    result={}
    sum=0
    for i in ciphertext:
        result[i]=ciphertext.count(i)
    for value in result.values():
        sum+=int(value)
    for key,value in result.items():
        result[key]=float(value)/sum   
    return result
        

if __name__ == '__main__':
    try:
        SingleSableSub(in_text)
    except EOFError:
        exit()

从这个图表中不难看出t的贡献率高达0.15,那我们可以大胆推测,t就是e的变换;再看其他的,会发现,o f的 贡献也很高。那可能是 t r a i n o的替换,那我们不妨来尝试一下。先把t换掉,然后再找出 o f的位置,可以看到of常常组合在一起,那可能的组合就有 tr ra ai in io 然后带进去看一看哪些比较合理,这里我们看到了一个组合ttf 也就是 ee_ 这里我们想一下常常和ee搭配的组合(当然是在第二步的基础上eer eea eei een eeo)可以发现een可能比较常见,那我们断定o 是 i 的变换 , f 是 n 的变换。填进去。也只能推出这么多了,因为我们选的样本太少了,不过可以看出,如果我进行大篇幅的加密,那么自然语言的特点就会越来越突出。

明文:i m g r o w i n g l i k e a s e e d r a i n s b e e n f a l l i n g o n m e

密文:o d u k g v o f u s o a t q l t t r k q o f l w t t f y q s s o f u g f d t

推测:i                 i  n       i    e     ee         i n       een           i n           e

拓展:可见如果这样加密,具有明显的统计学特征,那我们是不是可以变种方法。

1. 同音词替代,这是一种一对多的替代:举个例 e可以替换成为 a c d f 中的随机一个,这种方法大大增加了单个字符的使用频率分析难度,但是值得一提的是,它并没有改变自然语言结构,也就是说一些双音节组合,或者多音节组合的规律特征仍然存在。所以依然可以分析。

2. 对多个明文组合加密,例如: aes 加密为vse , 不单一对每个字符进行加密,可以大大减少明文在密文中的结构残留度。

3. 多表替代:即用多个表单进行替代


Playfair密码

这是一种多字母代替密码,它把明文中的双子母音节分组进行加密。

那么来介绍一下加密过程:

首先建立一个二维数组,一共有26个英文字母,那建立一个5*5的数组,然后选定一个key,将key去掉重复字母,依次填入数组,然后将剩余字母填入剩余部分,将 i 和 j 看作同一个字符,那么我们就会得到数组:(key='zhoieghiwe')

zhoi / je
gwabc
dfklm
npqrs
tuvxy

然后进行加密,加密规则:

  1. 将明文每两个字母组成一对。如果成对后有两个相同字母紧挨或最后一个字母是单个的,就插入一个字母,这里取x

  2. 若p1 p2在同一行,对应密文c1 c2分别是紧靠p1 p2 右端的字母。其中第一列被看做是最后一列的右方。如,按照前表,ac 对应 bg

  3.  

    若p1 p2在同一列,对应密文c1 c2分别是紧靠p1 p2 下方的字母。其中第一行被看做是最后一行的下方。如,按照前表,wp 对应 fu

 

若p1 p2不在同一行,不在同一列,则c1 c2是由p1 p2确定的矩形的其他两角的字母(至于横向替换还是纵向替换要事先约好,或自行尝试)(这里取该字母所在行为密文所在行)。如,按照前表,wr 对应 bp

那么我又又又写了个代码。。。

 

 

import re
import sys

in_text=sys.argv[1:]
P_E_S=['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']


def encryption(plaintext,matrix):
    ciphertext=[]
    for i in plaintext:
        if i[0] != 'j':
            ind_col_1=location(i[0],matrix)
        else:
            ind_col_1=location('i',matrix)
        if i[1] != 'j':
            ind_col_2=location(i[1],matrix)
        else:
            ind_col_2=location('i',matrix)
        if ind_col_1[0]==ind_col_2[0]:
            ciphertext.append(matrix[ind_col_1[0]][(ind_col_1[1]+1)%5])
            ciphertext.append(matrix[ind_col_2[0]][(ind_col_2[1]+1)%5])
        elif ind_col_1[1]==ind_col_2[1]:
            ciphertext.append(matrix[(ind_col_1[0]+1)%5][ind_col_1[1]])
            ciphertext.append(matrix[(ind_col_2[0]+1)%5][ind_col_2[1]])
        else:
            ciphertext.append(matrix[ind_col_1[0]][ind_col_2[1]])
            ciphertext.append(matrix[ind_col_2[0]][ind_col_1[1]])
    return ciphertext

def decryption(ciphertext,matrix):
    plaintext=[]
    for i in ciphertext:
        if i[0] != 'j':
            ind_col_1=location(i[0],matrix)
        else:
            ind_col_1=location('i',matrix)
        if i[1] != 'j':
            ind_col_2=location(i[1],matrix)
        else:
            ind_col_2=location('i',matrix)
        if ind_col_1[0]==ind_col_2[0]:
            plaintext.append(matrix[ind_col_1[0]][(ind_col_1[1]+4)%5])
            plaintext.append(matrix[ind_col_2[0]][(ind_col_2[1]+4)%5])
        elif ind_col_1[1]==ind_col_2[1]:
            plaintext.append(matrix[(ind_col_1[0]+4)%5][ind_col_1[1]])
            plaintext.append(matrix[(ind_col_2[0]+4)%5][ind_col_2[1]])
        else:
            plaintext.append(matrix[ind_col_1[0]][ind_col_2[1]])
            plaintext.append(matrix[ind_col_2[0]][ind_col_1[1]])
    i=0
    while i < len(plaintext)-1:
        if plaintext[i]=='x':
            if plaintext[i-1]==plaintext[i+1]:
                plaintext.pop(i)
        else:
            i+=1
    return plaintext

def playfair(in_text):
    print("此加密解密只涉及26个英文字母")
    print('---------------------------------')
    choice=input("1. 加密 2. 解密 ")
    key=input('密钥: ')
    in_text=''.join(in_text)
    in_text=re.compile('[^a-zA-Z]').sub('', in_text.lower())
    sublist=DataPre(in_text)
    AlphSubTable=ASTgenerator(key)

    if choice is '1':
        print('加密结果: '+''.join(encryption(sublist,AlphSubTable)))
    elif choice is '2':
        print("解密结果: "+''.join(decryption(sublist,AlphSubTable)))
    else:
        print("输入错误")

def DataPre(data):
    i=0
    sublist=[]
    while i < len(data):
        try:
            if data[i] != data[i+1]:
                sublist.append(data[i]+data[i+1])
                i=i+2
            else:
                sublist.append(data[i]+'x')
                i=i+1
        except:
            data=data+'x'
    return sublist

def ASTgenerator(key):
    mainkeys=[]
    for i in key:
        if i not in mainkeys:
            mainkeys.append(i)
    output=[]
    P_E_S_I=P_E_S
    for ind in range(5):
        line=[]
        col=0
        while col < 5:
            if len(mainkeys) != 0:
                pop_letter=P_E_S_I.pop(P_E_S_I.index(mainkeys.pop(0)))
                if pop_letter =='j':
                    continue
                else:
                    line.append(pop_letter)
                    col+=1
            elif len(P_E_S_I) != 0:
                pop_letter=P_E_S_I.pop(0)
                if pop_letter =='j':
                    continue
                else:
                    line.append(pop_letter)
                    col+=1
        output.append(line)
    return output

def location(letter,martix):
    for ind in range(5):
        for col in range(5):
            if martix[ind][col]==letter:
                return [int(ind),int(col)]

if __name__ == '__main__':
    try:
        playfair(in_text)
    except EOFError:
        exit()

 

优势:它用仅有的26个字母构造了26^2个字母对,因此识别出单个字母的难度更加困难。

劣势:仍然完好的保留了明文语言的大部分结构,相对容易破解。这里有字母出现的相对频率曲线作为参考。


 

 

 

 

 


  博主还在学习密码学的相关内容,如有错误,还请各位指出,谢谢支持。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值