欢迎光临
我们一直在努力

CTF学习/crypto/Playfair

Playfair

考点概念

Playfair 是首个广泛使用的双字母组替换密码

密钥字去重后接字母表(J 并入 I)按行填入 5×5 表。加密时明文两两成组,规则为:同行的两字母各取右侧邻居(行尾绕回行首)、同列各取下方邻居、不同行不同列取矩形对角位置的字母。

 

解密规则反向:同行取左邻、同列取上邻、对角规则与加密相同(互换后的对角)。明文预处理:两个相同字母相邻时中间插入填充字母(常为 X),长度为奇数时末尾补 X,故密文长度恒为偶数。

 

 

 

构表与运算规则:

1 构表:密钥字统一大小写、剔除重复字母,J 一律并入 I;密钥字母用完后按 a 至 z 顺序补齐剩余字母,仍跳过 J;25 个字母按行填满 5×5。

2 明文预处理:字母两两分组;组内两字母相同(如 balloon 中的 ll)在中间插填充字母 X 后重新分组,末尾落单补 X;数字与其他符号一般先剔除,纯字母成流。

3 同一行的两个字母,各取右侧一格的字母,行尾绕回行首;同一列的两个字母,各取下方一格的字母,列尾绕回列顶;不同行也不同列的两个字母构成矩形四角,首字母取自己所在行与对方所在列交点的字母,次字母同理取自己所在行与对方所在列交点的字母。 4 解密三规则:同行改为各取左侧(行首绕回行尾),同列改为各取上方(列顶绕回列底),矩形规则不变直接套用,因为取对角这个操作自身互逆。 5 预处理插入的填充字母保留在解密结果里,读明文时按英文语义剔除即可;密文长度恒为偶数且不出现相同字母相邻(同字母对在加密前已被拆开)。

 

 

5×5 矩阵构造与三种规则的双字母加密全过程

 

第一步构表:MONARCHY 无重复字母,按行填表得第 1 行 M O N A R、第 2 行 C H Y B D、第 3 行 E F G I K、第 4 行 L P Q S T、第 5 行 U V W X Z(J 并入 I,表内无 J)。

第二步同行加密:取明文组 MN,M 位于第 1 行第 1 列、N 位于第 1 行第 3 列,同在第 1 行,各取右侧邻居:M 取第 2 列的 O,N 取第 4 列的 A,MN 加密为 OA。解密 OA:同行取左邻,O 取第 1 列的 M,A 取第 3 列的 N,还原 MN。

第三步同列加密:取明文组 MC,M 位于第 1 行第 1 列、C 位于第 2 行第 1 列,同在第 1 列,各取下方邻居:M 取第 2 行第 1 列的 C,C 取第 3 行第 1 列的 E,MC 加密为 CE。解密 CE:同列取上邻,C 取第 1 行第 1 列的 M,E 取第 2 行第 1 列的 C,还原 MC。

第四步对角加密:取明文组 HI,H 位于第 2 行第 2 列、I 位于第 3 行第 4 列,行列皆不同,构成矩形四角。首字母 H 取 H 所在行(第 2 行)与 I 所在列(第 4 列)交点的 B;次字母 I 取 I 所在行(第 3 行)与 H 所在列(第 2 列)交点的 F。HI 加密为 BF。解密 BF:B 取 B 所在行与 F 所在列交点得 H,F 取 F 所在行与 B 所在列交点得 I,还原 HI,矩形规则加解密同式、天然互逆。

第五步合成示例:明文 PLAYFAIRDEMO 经预处理与逐组加密得本节脚本密文 QPNBIOKACKON,

 

识别特征

密文为偶数长度的纯字母串,且全文不出现字母 J

题面提示语出现 公平、fair、playfair 字样

题面给出一个密钥词;或明确说明双字母、成对加密

附件形式:txt 内一行偶数长度密文

凯撒、仿射、atbash、ROT13 全部失败后的下一候选

靶场题目

Cipher(公平的玩吧)

Writeup

 

 

题目:Cipher(BUUCTF) 题面:题目描述为"还能提示什么呢?公平的玩吧(密钥自己找)",密文 Dncnoqqfliqrpgeklwmppu,要求得到的 flag 请包上 flag{} 提交,且为 flag{小写字母}。 识别:密文 22 位为偶数长度、纯字母且全文无字母 J,命中识别特征第一、三条;提示语"公平的玩吧"对应英文 fair play,其中 play fair 正是 Playfair(普莱费尔)密码名的英文拼法,命中识别特征第二条,双重命中即可锁定 Playfair。

解题:第一步确定密钥,题目让选手自找,公开 writeup 一致取提示所指的算法名 playfair 本身作密钥;第二步构表:playfair 去重得 p l a y f i r,J 并入 I 后按字母表补齐,得第 1 行 P L A Y F、第 2 行 I R B C D、第 3 行 E G H K M、第 4 行 N O Q S T、第 5 行 U V W X Z,串接形式即 playfirbcdeghkmnoqstuvwxz;第三步把密文两两分组 Dn cn oq qf li qr pg ek lw mp pu,逐组按解密三规则还原(同行取左邻、同列取上邻、矩形取对角),得 itisnotaproblemhavefun;第四步小写套壳提交 flag{itisnotaproblemhavefun}

脚本:

#密钥 playfair 构表,逐组按解密规则还原
CIPHERTEXT = "Dncnoqqfliqrpgeklwmppu"
KEY = "playfair"
EXPECTED_PLAIN = "itisnotaproblemhavefun"
​
​
def build_table(key):
   seen = []
   for ch in (key.upper() + "ABCDEFGHIKLMNOPQRSTUVWXYZ"):
       if ch == "J":
           ch = "I"
       if ch not in seen:
           seen.append(ch)
   return [seen[i * 5:(i + 1) * 5] for i in range(5)]
​
​
def playfair_decrypt(ciphertext, key):
   t = build_table(key)
   pos = {c: (r, cc) for r, row in enumerate(t) for cc, c in enumerate(row)}
   assert len(ciphertext) % 2 == 0, "密文长度不是偶数"
   out = []
   for i in range(0, len(ciphertext), 2):
       a, b = ciphertext[i].upper(), ciphertext[i + 1].upper()
       assert a != b, f"同字母对 {a}{b},不合 Playfair 密文形态"
       ra, ca = pos[a]
       rb, cb = pos[b]
       if ra == rb:
           out.append(t[ra][(ca – 1) % 5] + t[rb][(cb – 1) % 5])
       elif ca == cb:
           out.append(t[(ra – 1) % 5][ca] + t[(rb – 1) % 5][cb])
       else:
           out.append(t[ra][cb] + t[rb][ca])
   return "".join(out)
​
​
def main():
   table = build_table(KEY)
   print("密钥表串接:", "".join("".join(row) for row in table).lower())
   plain = playfair_decrypt(CIPHERTEXT, KEY).lower()
   print("解密结果:", plain)

   print("最终结果:", "flag{" + plain + "}")
​
​
if __name__ == "__main__":
   main()

工具:PlayFair Cipher – Online Decoder, Encoder, Solver, Translator

脚本

# 密钥字构造 5×5 表(J 并入 I 格),双字母组按规则解密
#同行取左邻、同列取上邻、成矩形取对角(同行同列位置)
# CIPHERTEXT = 题目密文(长度为偶数)
# KEY = 题目密钥字;CTF 中常不给密钥,需枚举题目提示语中的词
​
CIPHERTEXT = "QPNBIOKACKON"
KEY = "MONARCHY"
EXPECTED = "PLAYFAIRDEMO"  # 预埋明文,用于自检
​
​
def build_table(key):
   # 密钥去重后接字母表(J 并入 I),按行填 5×5
   seen = []
   for ch in (key.upper() + "ABCDEFGHIKLMNOPQRSTUVWXYZ"):
       if ch == "J":
           ch = "I"
       if ch not in seen:
           seen.append(ch)
   return [seen[i * 5:(i + 1) * 5] for i in range(5)]
​
​
def playfair_decrypt(ciphertext, key):
   t = build_table(key)
   pos = {c: (r, cc) for r, row in enumerate(t) for cc, c in enumerate(row)}
   assert len(ciphertext) % 2 == 0, "密文长度不是偶数"
   out = []
   for i in range(0, len(ciphertext), 2):
       a, b = ciphertext[i], ciphertext[i + 1]
       assert a != b, f"同字母对 {a}{b},可能需要检查密文"
       ra, ca = pos[a]
       rb, cb = pos[b]
       if ra == rb:
           out.append(t[ra][(ca – 1) % 5] + t[rb][(cb – 1) % 5])
       elif ca == cb:
           out.append(t[(ra – 1) % 5][ca] + t[(rb – 1) % 5][cb])
       else:
           out.append(t[ra][cb] + t[rb][ca])
   return "".join(out)
​
​
def main():
   plain = playfair_decrypt(CIPHERTEXT, KEY)
   print(f"解密结果: {plain}")
   assert plain == EXPECTED, f"解密结果与预埋明文不一致: {plain}"
   print(f"最终结果: flag{{{plain}}}")
​
​
if __name__ == "__main__":
   main()

 

 

赞(0)
未经允许不得转载:171主机测评 » CTF学习/crypto/Playfair
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址