1.2 维吉尼亚密码python实现


1、有密钥字加密解密

引例:1.2.试用维吉尼亚密码加密明文串:

1.2.试用维吉尼亚密码加密明文串:
we are discovered , save yourself.
这里密钥字为: fridlay.

1.2 答案:
密文串为:BVIU EBN.J KRVC wVLV ATJP WXRQ JCN

版本一:带密钥字

# -*-coding:utf-8-*-
# 维吉尼亚

'''
fileName : main.py
'''

#import VigenereEncrypto
#import VigenereDecrypto

# -*-coding:utf-8-*-
# 维吉尼亚解密

'''
fileName : VigenereDecrypto.py
'''


def VigenereDecrypto(cipher,key):#解密
    message = ''
    non_alpha_count = 0
    for i in range (len(cipher)):#遍历
        if cipher[i].isalpha():
            if cipher[i].islower():
                offset = ord(key[(i - non_alpha_count) % len(key)]) - ord('a')
                message += chr((ord (cipher[i]) - ord('a') - offset) % 26 + ord('a'))
            else:
                offset = ord(key[(i - non_alpha_count) % len(key)]) - ord('a')
                message += chr((ord (cipher[i]) - ord('A') - offset) % 26 + ord('A')+32)        
        else:
            message += cipher[i]
            non_alpha_count += 1
    return message


# -*-coding:utf-8-*-
# 维吉尼亚加密

'''
fileName : VigenereEncrypto.py
'''


def VigenereEncrypto(message, key):
    cipher = ''
    non_alpha_count = 0
    for i in range (len(message)):#遍历
        if message[i].isalpha():
            if message[i].islower():
                offset = ord(key[(i - non_alpha_count) % len(key)]) - ord('a')
                cipher += chr((ord (message[i]) - ord('a') + offset) % 26 + ord('a')-32)
            else:
                offset = ord(key[(i - non_alpha_count) % len(key)]) - ord('a')
                cipher += chr((ord (message[i]) - ord('A') + offset) % 26 + ord('A'))        
        else:
            cipher += message[i]
            non_alpha_count += 1
    return cipher


print(u"维吉尼亚加密")

plainText = input("Please input the plainText : ")
key = input("Please input the key : ")

plainTextToCipherText = VigenereEncrypto(plainText, key)
print(u"加密后得到的暗文是 : " + plainTextToCipherText)

print(u"维吉尼亚解密")

cipherText =plainTextToCipherText
#input("Please input the cipherText : ")
key =key
#input("Please input the key : ")

cipherTextToPlainText = VigenereDecrypto(cipherText, key)
print(u"解密后得到的明文是 : " + cipherTextToPlainText)


# 维吉尼亚加密:
# Please input the plainText : we are discovered , save yourself.
# Please input the key : fridlay.
# 加密后得到的暗文是 : BV IUP DGTHFDHCEB , TFMM BZUPTJCN.

# 维吉尼亚解密:
# 解密后得到的明文是 : we are discovered , save yourself.

版本二:带密钥字

##########Vigenere密码############
# 有秘钥字加密解密
# 使用维吉尼亚密码加密明文串:
# we are discovered , save yourself.
# 秘钥字为:friday.

letter_list='ABCDEFGHIJKLMNOPQRSTUVWXYZ' #字母表
  
#根据输入的key生成key列表
def Get_KeyList(key):
    key_list=[]
    for ch in key:
        key_list.append(ord(ch.upper())-65)
    return key_list
  
#加密函数
def Encrypt(plaintext,key_list):
    ciphertext=""
    i=0
    for ch in plaintext: #遍历明文
        if 0==i%len(key_list):
            i=0
        if ch.isalpha(): #明文是否为字母,如果是,则判断大小写,分别进行加密
                if ch.isupper(): 
                    ciphertext+=letter_list[(ord(ch)-65+key_list[i]) % 26]
                    i+=1
                else:
                    ciphertext+=letter_list[(ord(ch)-97+key_list[i]) % 26].lower()
                    i+=1   
        else: #如果密文不为字母,直接添加到密文字符串里
                ciphertext+=ch
    return ciphertext
  
#解密函数
def Decrypt(ciphertext,key):
    plaintext=""
    
    i=0
    for ch in ciphertext: #遍历密文
        if 0==i%len(key_list):
            i=0
        if ch.isalpha(): #密文为否为字母,如果是,则判断大小写,分别进行解密
                if ch.isupper():
                    plaintext+=letter_list[(ord(ch)-65-key_list[i]) % 26]
                    i+=1
                else:
                    plaintext+=letter_list[(ord(ch)-97-key_list[i]) % 26].lower()
                    i+=1   
        else: #如果密文不为字母,直接添加到明文字符串里
            plaintext+=ch
    return plaintext
  
if __name__=='__main__':
    print("加密请按D,解密请按E:")
    user_input=input()
    while(user_input!='D' and user_input!='E'):#输入合法性判断
        print("输入有误!请重新输入:")
        user_input=input()
    
    print("请输入密钥:")
    key=input()
    while(False==key.isalpha()):#输入合法性判断
        print("输入有误!密钥为字母,请重新输入:")
        key=input()
        
    key_list=Get_KeyList(key)
    
    if user_input=='D':
        #加密
        print("请输入明文:")
        plaintext=input()

        ciphertext=Encrypt(plaintext,key_list)
        print("密文为:\n%s" % ciphertext)
    else:
        #解密
        print("请输入密文:")
        ciphertext=input()
        plaintext=Decrypt(ciphertext,key_list)
        print("明文为:\n%s" % plaintext)

  
# 加密请按D,解密请按E:
# D
# 请输入密钥:
# friday
# 请输入明文:
#  we are discovered , save yourself.
# 密文为:
#  bviu ebnj krvc wvlv atjp wxrq jcn.

---

2、无密钥字加密解密

引例3.1:破译下列用维吉尼亚密码加密的密文

3.1(书上习题1.21(b))破译下列用维吉尼亚密码加密的密文:KCCPKBGUFDPHQTYAVINRRTMVGRKDNBVFDETDGILTXRGUDDKOTFMBPVGEGLTGCKQRACQCWDNAWCRXIZAKFTLEWRPTYCQKYVXCHKFTPONCQQRHJVAJUWETMCMSPKQDYHJVDAHCTRLsvsKCGCZQQDzXGSFRL SwcwSJTBHAFSIASPRJAHKJRJUMVGKMITZHFPDISPZLVLGWTFPLKKEBDPGCEBSHCTJRWXBAFSPEZQNRWxcVYCGAONWDDKACKAWBBIKFTIOVKCGGHJVLNHIFFSQEsvYCLACNVRWBBIREPBBVFEXOSCDYGZWPFDTKFQIYCWHJVLNHIQIBTKHJVNPIST

3.1 答案:
keylength最可能为:m=6,密钥K=(2,17,24,15,19,14),CRYPTO,明文序列为:
i learned how to calculate the amount of paper needed for a room when i was at school you multiply the square foot age of the walls by the cubic contents of the floor and ceiling combined and double it you then allow half the total for openings such as windows and doors then you allow the other half for matching the pattern then you double the whole thing again to give a margin of error and then you order the paper.

版本一 - 无密钥字

import vigenerecipher

#使用拟重合指数法确定秘钥长度:拟重合指数大于0.6为标志
def length(Ciphertext):
    ListCiphertext=list(Ciphertext)
    Keylength=1
    while True:
        #指数初始化为0
        CoincidenceIndex = 0
        #使用切片分组
        for i in range(Keylength):
            Numerator = 0
            PresentCipherList = ListCiphertext[i::Keylength]
            #使用集合去重,计算每一子密文组的拟重合指数
            for Letter in set(PresentCipherList):
                Numerator += PresentCipherList.count(Letter) * (PresentCipherList.count(Letter)-1)
            CoincidenceIndex += Numerator/(len(PresentCipherList) * (len(PresentCipherList)-1))
        #求各子密文组的拟重合指数的平均值
        Average=CoincidenceIndex / Keylength
        Keylength += 1
        #均值>0.6即可退出循环
        if Average > 0.06:
            break
    Keylength -= 1
    return Keylength

#使用重合指数法确定秘钥内容:遍历重合指数的最大值为标志
def keyword(Ciphertext,keylength):
    ListCiphertext = list(Ciphertext)
    Standard = {'A':0.08167,'B':0.01492,'C':0.02782,'D':0.04253,'E':0.12702,'F':0.02228,'G':0.02015,'H':0.06094,'I':0.06966,'J':0.00153,'K':0.00772,'L':0.04025,'M':0.02406,'N':0.06749,'O':0.07507,'P':0.01929,'Q':0.00095,'R':0.05987,'S':0.06327,'T':0.09056,'U':0.02758,'V':0.00978,'W':0.02360,'X':0.00150,'Y':0.01974,'Z':0.00074}
    while True:
        KeyResult = []
        for i in range(keylength):
            # 使用切片分组
            PresentCipherList = ListCiphertext[i::keylength]
            #初始化重合指数最大值为0,检验移动位数对应字符以*代替
            QuCoincidenceMax = 0
            KeyLetter = "*"
            #遍历移动的位数
            for m in range(26):
                #初始化当前移动位数的重合指数为0
                QuCoincidencePresent = 0
                #遍历计算重合指数:各个字符的频率*对应英文字符出现的标准频率---的和
                for Letter in set(PresentCipherList):
                    LetterFrequency = PresentCipherList.count(Letter) / len(PresentCipherList)
                    # 标准频率
                    k = chr( ( ord(Letter) - 65 - m ) % 26 + 65 )
                    StandardFrequency = Standard[k]
                    #计算重合指数
                    QuCoincidencePresent = QuCoincidencePresent + LetterFrequency * StandardFrequency
                #保存遍历过程中重合指数的最大值,同时保存对应应对的位数,即对应key的字符
                if QuCoincidencePresent > QuCoincidenceMax:
                    QuCoincidenceMax = QuCoincidencePresent
                    KeyLetter = chr( m + 65 )
            #保存当前位置key的值,退出循环,进行下一组子密文移动位数的尝试
            KeyResult.append( KeyLetter )
        #列表转为字符串
        Key = "".join(KeyResult)
        break
    return Key

if __name__ == '__main__':
    Ciphertext = input("输入密文:").upper()
    Keylength = length(Ciphertext)
    print("keylength最可能为:",Keylength)
    KeyResult = keyword(Ciphertext,Keylength)
    print("秘钥最可能为:" , KeyResult)
    #已知秘钥可用python自带维吉尼亚解密
    ClearText = vigenerecipher.decode( Ciphertext,KeyResult )
    print("解密结果为:" , ClearText)

# keylength最可能为: 6
# 秘钥最可能为: CRYPTO
# 解密结果为: i learned how to calculate the amount of paper needed for a room when i was at school you multiply the square foot age of the walls \
# by the cubic contents of the floor and ceiling combined and double it you then allow half the total for openings such as windows and doors \
# then you allow the other half for matching the pattern then you double the whole thing again to give a margin of error and then you order the paper.

版本二 - 无密钥字

# -*- coding: utf-8 -*-  
'''
代码中各个函数的说明如下
gcd(a, b) #获取两个数的最大公因数
multi_gcd(num) #获取数组中所有数的最大公因数
find_substr(strs) #统计字符串中长度为2的子串出现的次数
cal_pos(substr, strs, count) #计算字符串中,子串出现位置的距离差
cal_frequency(strs) #统计字符串中,英文字符出现的频率,并计算出 M_n
str_offset(strs, n) #将字符串中的所有英文字符整体向左偏移n位
crack(strs, m)  #计算密钥和明文的主体函数
'''
import operator
from functools import reduce
strs = "KCCPKBGUFDPHQTYAVINRRTMVGRKDNBVF\
DETDGILTXRGUDDKOTFMBPVGEGLTGCKQRACQCWDN\
AWCRXIZAKFTLEWRPTYCQKYVXCHKFTPONCQQRHJV\
AJUWETMCMSPKQDYHJVDAHCTRLSVSKCGCZQQDZXGS\
FRLSWCWSJTBHAFSIASPRJAHKJRJUMVGKMITZHFP\
DISPZLVLGWTFPLKKEBDPGCEBSHCTJRWXBAFSPEZ\
QNRWXCVYCGAONWDDKACKAWBBIKFTIOVKCGGHJVLN\
HIFFSQESVYCLACNVRWBBIREPBBVFEXOSCDYGZWP\
FDTKFQIYCWHJVLNHIQIBTKHJVNPIST"
def gcd(a, b):
    return gcd(b,a%b) if b!=0 else a
def multi_gcd(num):
    return reduce(gcd,num)
def find_substr(strs):
    ans={}
    for i in range(len(strs) - 1):
        substr = strs[i:i + 2]
        count=0
        if substr not in ans.keys():
            for j in range(len(strs) - 1):
                if substr == strs[j:j + 2]:
                    count=count+1;     
            ans[substr] = count
    return ans
def cal_pos(substr, strs, count):
    ans = []
    last_pos=0
    for i in range(count):
        temp=last_pos
        last_pos = strs.find(substr, temp+1)
        #print(last_pos)
        if i != 0:
            ans.append(last_pos - temp)
    return ans
def cal_frequency(strs):
    p = [0.082, 0.015, 0.028, 0.043, 0.127, 0.022, 0.02,
     0.061, 0.07, 0.002, 0.008, 0.04, 0.024, 0.067,
     0.075, 0.019, 0.001, 0.06, 0.063, 0.091, 0.028,
     0.01, 0.023, 0.001, 0.02, 0.001]
    frequency = [strs.count(chr(ord('A') + i)) for i in range(26)]
    ans=sum((frequency[i]*p[i]/ len(strs)) for i in range(len(frequency)))
    return ans
def str_offset(strs, n):
    return "".join([chr((ord(i)-ord('A')-n)%26+ord('A'))for i in strs])
def crack(strs, m):
    caesar_substr = [strs[i::m] for i in range(m)]
    test_frequency = [[cal_frequency(str_offset(caesar_substr[i], j)) for j in range(26)] for i in range(len(caesar_substr))]
    key = "".join([chr(test_frequency[i].index(max(test_frequency[i])) + ord('A')) for i in range(len(test_frequency))])
    #print(test_frequency)
    #caesar_plain = [str_offset(caesar_substr[i], ord(key[i]) - ord('A')) for i in range(m)]
    plaintext="".join([chr((ord(strs[i])-ord(key[i%6]))%26+ord('A')) for i in range(len(strs))])
    return key,plaintext
dictionary = find_substr(strs)
pos_data1= cal_pos('HJ', strs, dictionary['HJ'])
pos_data2 = cal_pos('JV', strs, dictionary['JV'])
pos_data3 = cal_pos('KF', strs, dictionary['KF'])
pos_data1.extend(pos_data2)
pos_data2.extend(pos_data3)
key, plaintext = crack(strs, multi_gcd(pos_data1))
print('dictionary data:', sorted(dictionary.items(), key=operator.itemgetter(1), reverse=True),'\n')
print('The most frequent substrs ars \'HJ\'and\'JV\'')
print('The GCD of the two substrs location distance are all',multi_gcd(pos_data1))
print('So we can assume that the length of key is ',multi_gcd(pos_data1))
print('key is', key)
print('The plain text is :',plaintext.lower())
#仿射希尔密码.py

# dictionary data: [('HJ', 5), ('JV', 5), ('KF', 4), ('YC', 4), ('SP', 4), ('KC', 3), ('FD', 3), ('VG', 3), ('GC', 3), ('AC', 3), ('CQ', 3), ('CW', 3), ('FT', 3), ('CG', 3), ('FS', 3), ('VL', 3), ('RW', 3), ('BB', 3), ('PK', 2), ('GU', 2), ('DP', 2), ('TY', 2), ('NR', 2), ('TM', 2), ('MV', 2), ('DN', 2), ('BV', 2), ('VF', 2), ('ET', 2), ('LT', 2), ('DD', 2), ('DK', 2), ('TF', 2), ('CK', 2), ('KQ', 2), ('QR', 2), ('WD', 2), ('AW', 2), ('WC', 2), ('XC', 2), ('HK', 2), ('ON', 2), ('QQ', 2), ('JU', 2), ('QD', 2), ('DY', 2), ('AH', 2), ('HC', 2), ('CT', 2), ('RL', 2), ('LS', 2), ('SV', 2), ('ZQ', 2), ('AF', 2), ('RJ', 2), ('JR', 2), ('FP', 2), ('IS', 2), ('EB', 2), ('WX', 2), ('VY', 2), ('KA', 2), ('WB', 2), ('BI', 2), ('LN', 2), ('NH', 2), ('HI', 2), ('TK', 2), ('QI', 2), ('CC', 1), ('CP', 1), ('KB', 1), ('BG', 1), ('UF', 1), ('PH', 1), ('HQ', 1), ('QT', 1), ('YA', 1), ('AV', 1), ('VI', 1), ('IN', 1), ('RR', 1), ('RT', 1), ('GR', 1), ('RK', 1), ('KD', 1), ('NB', 1), ('DE', 1), ('TD', 1), ('DG', 1), ('GI', 1), ('IL', 1), ('TX', 1), ('XR', 1), ('RG', 1), ('UD', 1), ('KO', 1), ('OT', 1), ('FM', 1), ('MB', 1), ('BP', 1), ('PV', 1), ('GE', 1), ('EG', 1), ('GL', 1), ('TG', 1), ('RA', 1), ('QC', 1), ('NA', 1), ('CR', 1), ('RX', 1), ('XI', 1), ('IZ', 1), ('ZA', 1), ('AK', 1), ('TL', 1), ('LE', 1), ('EW', 1), ('WR', 1), ('RP', 1), ('PT', 1), ('QK', 1), ('KY', 1), ('YV', 1), ('VX', 1), ('CH', 1), ('TP', 1), ('PO', 1), ('NC', 1), ('RH', 1), ('VA', 1), ('AJ', 1), ('UW', 1), ('WE', 1), ('MC', 1), ('CM', 1), ('MS', 1), ('YH', 1), ('VD', 1), ('DA', 1), ('TR', 1), ('VS', 1), ('SK', 1), ('CZ', 1), ('DZ', 1), ('ZX', 1), ('XG', 1), ('GS', 1), ('SF', 1), ('FR', 1), ('SW', 1), ('WS', 1), ('SJ', 1), ('JT', 1), ('TB', 1), ('BH', 1), ('HA', 1), ('SI', 1), ('IA', 1), ('AS', 1), ('PR', 1), ('JA', 1), ('KJ', 1), ('UM', 1), ('GK', 1), ('KM', 1), ('MI', 1), ('IT', 1), ('TZ', 1), ('ZH', 1), ('HF', 1), ('PD', 1), ('DI', 1), ('PZ', 1), ('ZL', 1), ('LV', 1), ('LG', 1), ('GW', 1), ('WT', 1), ('PL', 1), ('LK', 1), ('KK', 1), ('KE', 1), ('BD', 1), ('PG', 1), ('CE', 1), ('BS', 1), ('SH', 1), ('TJ', 1), ('XB', 1), ('BA', 1), ('PE', 1), ('EZ', 1), ('QN', 1), ('CV', 1), ('GA', 1), ('AO', 1), ('NW', 1), ('IK', 1), ('TI', 1), ('IO', 1), ('OV', 1), ('VK', 1), ('GG', 1), ('GH', 1), ('IF', 1), ('FF', 1), ('SQ', 1), ('QE', 1), ('ES', 1), ('CL', 1), ('LA', 1), ('CN', 1), ('NV', 1), ('VR', 1), ('IR', 1), ('RE', 1), ('EP', 1), ('PB', 1), ('FE', 1), ('EX', 1), ('XO', 1), ('OS', 1), ('SC', 1), ('CD', 1), ('YG', 1), ('GZ', 1), ('ZW', 1), ('WP', 1), ('PF', 1), ('DT', 1), ('FQ', 1), ('IY', 1), ('WH', 1), ('IQ', 1), ('IB', 1), ('BT', 1), ('KH', 1), ('VN', 1), ('NP', 1), ('PI', 1), ('ST', 1)]

# The most frequent substrs ars 'HJ'and'JV'
# The GCD of the two substrs location distance are all 6
# So we can assume that the length of key is  6
# key is CRYPTO
# The plain text is : 
#ilearnedhowtocalculatetheamountofpaperneededforaroomwheniwasatschoolyoumultiplythesquarefootageofthewallsbythecubiccontentsofthefloorandceilingcombined \
# anddoubleityouthenallowhalfthetotalforopeningssuchaswindowsanddoorsthenyouallowtheotherhalfformatchingthepatternthenyoudoublethewholethingagain\
# togiveamarginoferrorandthenyouorderthepaper

```python


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM