深入浅出密码学第一章课后题(持续更新)
若有不足之处,请多多指教!
1.1(频率分析攻击)
密文如下
lrvmnir bpr sumvbwvr jx bpr lmiwv yjeryrkbi jx qmbm wibpr xjvni mkd ymibrut jx irhx wi bpr riirkvr jx ymbinlmtmipw utn qmumbr dj w ipmhh but bj rhnvwdmbr bpr yjeryrkbi jx bpr qmbm mvvjudwko bj yt wkbrusurbmbwjk lmird jk xjubt trmuijx ibndt
Wb wikjb mk rmit bmiq bj rashmwk rmvp yjeryrkb mkd wbi iwokwxwmkvr mkdijyr ynib urymwk nkrashmwkrd bj ower m vjyshrbr rashmkmbwjkjkr cjnhd pmer bjlr fnmhwxwrd mkd wkiswurd bj invp mk rabrkb bpmb pr vjnhd urmvp bpr ibmbr jx rkhwopbrkrd ywkd vmsmlhr jx urvjokwgwko ijnkdhrii ijnkd mkd ipmsrhrii ipmsr wdj kjb drry ytirhx bpr xwkmh mnbpjuwbt lnb yt rasruwrkvr cwbp qmbm pmi hrxb kjdjnlb bpmb bpr xjhhjcwko wi bpr sujsru msshwvmbwjk mkd wkbrusurbmbwjk w jxxruyt bprjuwri wk bpr pjsr bpmb bpr riirkvr jx jqwkmcmk qmumbr cwhh urymwk wkbmvb
使用python脚本,在kali中进行频率分析。
import sys,collections,pathlib
# SYS:读取命令行参数 COLLECTIONS:计数 PATHLIB:面向对象的文件路径操作
text=pathlib.Path(sys.argv[1]).read_text(errors='ignore')
# sys.argv[1]:终端里写的第一个参数 pathlib.Path:把字符串转换成路径对象
# .read_text:一次性把整个文件读成字符串 errors='ignore':遇到非法字符直接跳过,防止编码报错
letters =[ch.lower() for ch in text if ch.isalpha()]
# ch.isalpha():只保留字母
# ch.lower() :字母统一转小写total=len(letters)
# 列表长度=字母总数
freq=collections.Counter(letters)
# Counter:自动把列表里每个元素做频次统计
# for ch in sorted(freq): # sorted(freq):按字母顺序排
for ch, count in freq.most_common():#按频次排print(f"{ch}{freq[ch]/total:.4f}")# 直方图(kali中的命令行)# print(f"{ch} {freq[ch] / total}")# ./ freq.py cipher.txt | gnuplot - p - e "# set boxwidth 0.5; set style fill solid;# set xlabel 'Letter';set ylabel'Relative frequency';# plot '<cat' using 2: xtic(1) with boxes notitle"
得到频率分布如下:
和1.2.2表1-1进行对比,得
这里用了一个新的脚本,把频率统计出来后,将相应的字母进行替换。但是由于文本较短,对应的并不准确。
import sys ,collections,pathlib,string
# string:避免手打
cipher=pathlib.Path(sys.argv[1]).read_text(errors='ignore')
letters=[ch.lower() for ch in cipher if ch.isalpha()]freq=collections.Counter(letters)
cipher_order=[ch for ch,_ in freq.most_common()]
# most_common():按出现次数降序排序standard_order = 'etaoinshrdlcumwfgypbvkjxqz'
# 按英文标准顺序subst={}
for c,p in zip(cipher_order,standard_order):subst[c]=p
#zip:把两条顺序“粘在”一起,对应起来for ch in string.ascii_lowercase:subst.setdefault(ch,ch)
# setdefault:仅在键不存在时才写,没有出现过的字母保留自身映射,这样subst里面一定包含26个小写字母plain=[]
for ch in cipher:if ch.isalpha():out=subst[ch.lower()]plain.append(out.upper() if ch.isupper() else out)# 大小写还原:若原字母是大写就输出大写else:plain.append(ch)# 空格、标点、数字原封不动print(' '.join(plain))
最终原文如下:
1.2(移位密码)
密文如下:
Xultpaajcxitltlxaarpjhtiwtgxktghidhipxciwtvgtpilpitghlxiwiwtxgqadds.
使用暴力破解 caesar_brute.py
import pathlib, sysdef shift_char(ch, k):if 'A' <= ch <= 'Z':return chr((ord(ch) - ord('A') + k) % 26 + ord('A'))if 'a' <= ch <= 'z':return chr((ord(ch) - ord('a') + k) % 26 + ord('a'))return chcipher = pathlib.Path(sys.argv[1]).read_text(errors='ignore')for k in range(26):plain = ''.join(shift_char(ch, k) for ch in cipher)print(f'--- key = {k:02d} ---')print(plain)
在26中结果中找到能读的