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