ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

栅栏密码手搓教程:从换位原理到加密解密实战

栅栏密码手搓教程:从换位原理到加密解密实战 你如果刷过古典密码相关的帖子大概率见过“栅栏密码”这个名字。我第一次对它产生兴趣是收到一个同学手写的加密纸条上面没有换字母只把原文顺序打乱了再接在一起当时觉得特别神秘。后来自己对照资料研究才发现这个工具上手极快不用查表、不用算矩阵拿支笔在纸上写写画画就能完成加密和解密。“栅栏密码”最吸引人的地方正是它那种“换位置”的思路——明明字母还是那些字母但整体顺序一乱陌生感立刻就出来了。这篇文章主要面向三类人刚接触密码学、想找几个古典密码练手的学生想给好友或社团设计趣味暗号的爱好者以及做密码课程设计、需要演示“换位密码”原理的开发者。我会从最基础的机制讲起用3栏案例把加密、解密、排错、扩展全流程说透。所谓“3栏”就是栅栏密码里最经典、也最适合手工演示的栏数下文几乎所有例子都按这个配置展开。1. 栅栏密码的本质不是换字母而是重新排队1.1 密码学里的两条大路替换与换位想理解栅栏密码先要明白密码学里有两条基础路线。一条叫“替换密码”典型代表是凯撒密码把每个字母按字母表平移若干位比如“HELLO”变成“IFMMP”。这类密码的特点是字母本身变了但数量、位置关系大体不变。另一条叫“换位密码”栅栏密码就属于这一类每个字符都保持原样只是按某种规则重新排列位置。用大白话说替换密码是给每个人化个妆换位密码是让一群人重新排队。栅栏密码做的是后者。这个区别很重要因为它直接决定了密文的特征。替换后的密文字母统计频率通常还保留着自然语言的痕迹而栅栏密码的密文字母种类和个数与明文完全相同只是顺序被打乱。你要是拿栅栏加密一句英文再把加密前后的字频统计出来对比会发现每个字母出现的次数一模一样这正是换位密码最典型的“指纹”。1.2 “栅栏”这个名字从哪来“栅栏”在密码学里指的不是某个具体工具而是一个意象好几根竖直的栏杆并排立着需要在它们之间来回穿行。如果把明文里的字符串想象成一个移动的物体那么它的运动轨迹就像在栏杆之间绕来绕去一会儿往左偏一会儿往右偏最终形成一条“之”字形折线。顺带说一句读音。汉字“栅”有两个读音读“zhà”时指栅栏、围栏读“shān”时多用于地名或“栅极”这类科技词。密码学里的“栅栏密码”按“zhà栏密码”读才对。有些老教材也会写成“篱笆密码”指的东西完全一样只是翻译叫法不同。1.3 为什么大家都爱说“3栏”搜索引擎的热词里“栅栏密码3栏”几乎是标配。仔细想想主要有三个原因。第一教科书习惯用3栏做入门演示。英文教材里最常引用的例子就是把“WEAREDISCOVEREDFLEEATONCE”用3条轨道加密成“WECRLTEERDSOEEFEAOCAIVDEN”这个例子流传极广中文资料也爱引用很多人一搜就搜到了它。第二3栏正好能展示出“之”字形的完整节奏。栏数为3时路径是“上栏-中栏-下栏-中栏-上栏”往返短短5个字符就能形成一个完整周期既不单调又不会太冗长。如果只有2栏加密效果差不多等于把字符串拆成奇偶两组太直白如果栏数取5、6甚至更多画图和工作量又会明显增加新手容易看花眼。第三3栏在娱乐场景里足够用。给朋友写纸条、出谜题、布置寻宝线索3栏产生的混乱程度已经能让普通人一时看不出来解密又不会难到把人劝退。所以我后面的所有手搓演示都以R3为基线只在涉及通用公式的地方把R换成变量方便你举一反三。2. 手搓之前必须搞懂的机制之字形轨道的分配规律2.1 把每个字符轮流送进三条轨道我们在纸上画出三条平行的横线编号分别叫轨道0、轨道1、轨道2。加密时明文的第一个字符放在轨道0第二个字符放在轨道1第三个字符放在轨道2第四个字符回到轨道1第五个字符回到轨道0……以此类推。如果你把这个过程画出来字符走过的路径就是一条反复折返的“之”字线像一条蛇在三条栏杆之间上下穿行。走到最上面的轨道0后下一步一定去向轨道1走到最下面的轨道2后下一步也一定回到轨道1。轨道0和轨道2相当于两个折返点中间那根轨道承担着“连接上下”的任务。一个立刻能看出来的规律是轨道0和轨道2都是每隔固定间隔才出现一次字符而轨道1几乎每个字符位置都被它“接管”了一部分所以轨道1的字符数量往往明显多于上下两根轨道。这一点对后面手动解密时的切片长度判断特别关键。2.2 用一张小表看清“位置-轨道”的关系拿明文字符串“HELLOWORLD”来做演示。去掉空格之后字符串长度是10字符顺序如下位置下标0123456789字符HELLOWORLD所在轨道0121012101建议你按这个表在纸上实际走一遍从0号位置开始依次写下0、1、2、1、0、1、2、1、0、1。这串数字就是“之”字形路径的轨道编号序列。把它翻译成轨道的实际内容就是轨道0H、O、L轨道1E、L、W、R、D轨道2L、O2.3 周期公式T2×(R-1)如果只看文字描述你可能会以为每次都要模拟蛇形路径才能知道某个字符去哪条轨道。其实不必。对于栏数为R的栅栏密码同一个轨道连续两次拿到字符的位置间隔是固定的这个间隔叫“周期”公式是T 2 × (R - 1)当R3时T4。你回头去看表格轨道0在位置0、4、8出现间隔正是4轨道1的位置1、3、5、7、9虽然相邻间隔是2但每隔4个位置也会回到同一根轨道的同一个“波峰”或“波谷”位置。换个角度理解如果R是3那么明文中序号除以4余0的字符在轨道0余1或余3的字符在轨道1余2的字符在轨道2。这其实是一种更数学化的表达手工画图时不强制要求但一旦遇到超长字符串或者后续想写代码验证时这个公式能帮你省下大量画线时间。3. 手搓加密实操从一行明文到乱序密文3.1 加密前的准备工作手搓栅栏密码前先把规则说好不然后面一步乱步步乱。第一步去空格。英文短语里的空格在栅栏过程里会被当成一个普通位置参与排序但如果接收方不知道原空格位置解密回来就无法准确还原断句。因此常见约定是加密前删除所有空格例如“HELLO WORLD”在加密时写作“HELLOWORLD”。当然你也可以保留空格但必须额外记一张“空格位置表”这对手工操作来说很麻烦不推荐。第二步统一大小写。栅栏密码本身不区分大小写但手工处理时大小写混在一起容易看错建议全部转成大写或小写后再操作。第三步去掉标点。逗号、句号、问号如果混在密文里也会占据字符位置并造成冗余。如果是中文文本可以先把标点记在文末备注里或者干脆删掉。第四步可选密文分组。实际传抄时可以把密文每4个或5个字符一组写出来方便发送。比如密文“HOLELWRDLO”可以写成“HOLE LWRD LO”这只是展示格式不影响真正的密文内容。3.2 方法一写三条跑道逐个分发字符这是我最推荐新手使用的方法因为它最直观几乎不可能搞错轨道号。在纸上写三行轨道0______ 轨道1______ 轨道2______然后拿着明文“HELLOWORLD”从第一个字符开始按“0→1→2→1→0→1→2→1→0→1”的顺序把字符依次填到对应轨道后面轨道0HOL → 整理后是HOL轨道1ELWRD → 整理后是ELWRD轨道2LO → 整理后是LO最后从最上面的轨道0开始把三条跑道的内容首尾相接HOL ELWRD LO HOLELWRDLO这就是密文。整个过程里你只做了两件事先按“之”字的路线把字符分配到三个轨道再从上到下把轨道结果串联起来。加密结束。3.3 方法二直接画栅栏图如果你觉得“写三条跑道”还不够直观那就直接画完整的栅栏图。在纸上画三条水平线间距差不多就行。从左往右画一条“之”字形折线从最上面那条线开始斜向下到中间线再斜向下到最下面线然后斜向上回到中间线再斜向上回到最上面线以此类推。折线的每一个拐点对应明文中一个字符的位置你按照明文字符顺序把字符写到拐点旁边。画完之后栅栏图里有三层字符。你只要按从上到下的顺序逐行抄下所有字符得到的就是同一个密文“HOLELWRDLO”。这个方法的好处是能看到完整的路径适合第一次接触栅栏密码的人建立直觉。缺点是字符多、轨道多时图纸会比较乱需要你画得整齐些。3.4 方法三用周期公式直接抽字符如果你已经掌握前面说的周期公式T2×(R-1)那连画图都可以省了。R3时T4。以“HELLOWORLD”为例轨道0从位置0开始每隔4个取一个字符位置0、4、8分别是H、O、L → HOL轨道1位置1、3、5、7、9分别是E、L、W、R、D → ELWRD轨道2从位置2开始每隔4个取一个字符位置2、6分别是L、O → LO然后把三个轨道合并得到“HOLELWRDLO”。这几种方法本质是同一件事只是视角不同。我建议先用跑道法建立正确手感再用公式法做交叉验证。如果两种方法得到的密文一致通常就不会出错。3.5 中文示例今晚吃烧烤很多朋友关心栅栏密码能不能处理中文。答案是完全可以。中文没有空格天然省去了去空格这一步只要你把每个汉字当一个字符看待就行。明文“今晚吃烧烤”一共5个字栏数R3。按轨道分配轨道0位置0的“今”、位置4的“烤” → “今烤”轨道1位置1的“晚”、位置3的“烧” → “晚烧”轨道2位置2的“吃” → “吃”把三个轨道从上到下拼接密文 今烤晚烧吃如果你把这个密文发给朋友不提前说明规律对方确实很难猜到原文。解密时再把密文“今烤晚烧吃”拆成三段“今烤”“晚烧”“吃”沿“0→1→2→1→0”的路径依次取字就能恢复“今晚吃烧烤”。中文做栅栏的好处是每个字天然是独立原子不会像英文那样还要考虑单词分割。缺点则是如果原文特别长手写时可能不好对位。这时候建议把汉字序号标出来逐个数着走。3.6 经典英文案例复现假如你在网上搜“栅栏密码”或者“Rail Fence Cipher”十有八九会撞见下面这个经典案例明文WEAREDISCOVEREDFLEEATONCE栏数3密文WECRLTEERDSOEEFEAOCAIVDEN我用跑道法亲手做了一遍过程如下。明文长度25去掉空格后依然25。依次按轨道号填充。轨道0取位置0、4、8、12、16、20、24的字符W、E、C、R、L、T、E得到“WECRLTE”。轨道1取位置1、3、5、7、9、11、13、15、17、19、21、23的字符E、R、D、S、O、E、E、F、E、A、O、C得到“ERDSOEEFEAOC”。轨道2取位置2、6、10、14、18、22的字符A、I、V、D、E、N得到“AIVDEN”。三段相接正好得到“WECRLTEERDSOEEFEAOCAIVDEN”。如果你在网上看到的密文和这个略有出入例如有多余空格或大小写不同先检查是不是在分组展示环节加上了格式。4. 手搓解密实操逆向就是把散装的字符重新归位4.1 解密前必做的三项准备解密本质上是加密的逆过程。拿到密文后先别急着恢复原文做三个动作第一数清密文的总字符数n。注意这里要把所有字符都算上包括可能存在的空格或标点如果它们参与了加密过程。第二确认栏数R。如果是你自己加密的直接沿用加密时的栏数。如果是别人给你的密文且对方没说明栏数你需要先猜测R后面我会单独讲猜栏数的方法。第三算出周期T2×(R-1)然后推算出每条轨道的字符数量。这三步做完你就知道了密文应该如何切分到各条轨道。4.2 方法一切片回归法这是最推荐的手动解密方法因为它只需要一次切分、一次按路径读回逻辑非常清晰。仍以密文“HOLELWRDLO”为例。已知R3、n10、T4。第1步计算每条轨道的字符个数。轨道0占据的位置是0、4、8共3个轨道1占据的位置是1、3、5、7、9共5个轨道2占据的位置是2、6共2个。第2步把密文按顺序切块。前3个字符“HOL”是轨道0的内容接下来5个字符“ELWRD”是轨道1的内容最后2个字符“LO”是轨道2的内容。第3步按“0→1→2→1→0→1→2→1→0→1”的轨道路径从每条轨道里依次取字符。位置0取轨道0的第一个字符H位置1取轨道1的第一个字符E位置2取轨道2的第一个字符L位置3取轨道1的第二个字符L位置4取轨道0的第二个字符O……一直取到最后一个字符D。最终得到“HELLOWORLD”解密完成。这个方法最需要注意的地方是第1步和第2步的时间顺序关系必须先算准每条轨道的长度再切分密文顺序不能反。4.3 方法二先画网格再按位置回填如果你担心直接在脑内切块容易串位可以在纸上画出完整的网格。先画一个“之”字形的轨道图轨道0、1、2各分配到几排空格。空格的排列方式与加密时的栅栏图完全一致。然后在每个空格里标上轨道编号数一数每个轨道有几格。接着把密文从左到右、从上到下填入空格。最后从左到右、按“之”字路径把空格里的字符读出来。这个方法看起来多了一道工序但它把你“切分轨道”的数学操作变成了“往格子里填字符”的机械操作。对于席位特别多、栏数比较大的情况能有效预防手工切片时数错行。4.4 方法三原路返回法如果你在加密时习惯画栅栏图那解密时可以直接把那条折线反向走一遍先把密文分成三段分别放到轨道0、轨道1、轨道2对应的线路上然后沿着“之”字折线从左上到右下依次读取每个点位的字符。原路返回法和位置回填法的区别只在于一个先填格子再看格子一个直接把字符铺到折线路上再沿路收集。论效率二者差不多你可以按自己习惯选。4.5 经典密文完整逆推把经典密文“WECRLTEERDSOEEFEAOCAIVDEN”逆推回去来验证整个思路。已知R3n25T4。轨道0长度7轨道1长度12轨道2长度6。把密文切块轨道0WECRLTE轨道1ERDSOEEFEAOC轨道2AIVDEN然后按“0→1→2→1→0→1→2→1→0……”的路径取字。我按位置拆解一遍关键步骤位置0取轨道0的W位置1取轨道1的E位置2取轨道2的A位置3取轨道1的R位置4取轨道0的E……直到全部取完。恢复结果就是“WEAREDISCOVEREDFLEEATONCE”。如果你拿到的是不带空格的“WEAREDISCOVEREDFLEEATONCE”这是正常的原句空格需要你根据英文语义自己补上。每次解密完成后建议做一个反向校验把解密得到的明文再按相同栏数加密一次如果得到的密文和你最初的输入完全一致基本可以断定解密无误。手工操作时这个回环校验比单纯“感觉读得通”更可靠。4.6 只有密文、不知道栏数怎么办在实际挑战题里通常不会有人好心告诉你“这是几栏的栅栏”。这时需要猜栏数。栏数R最小区间是2最大理论上可以到字符总数但实际手工爆破时建议从2一直试到10左右就足够了。每试一个R值按前面解密流程还原一次明文判断结果是否有意义。更高效的做法是把可能的R值从小到大跑一遍同时判断每个候选结果的“自然语言特征”。对英文来说看是否出现常见单词结构对中文来说直接看语义是否通顺。如果一个R值还原出来的结果完全不成句换下一个R值继续试。如果密文很长手工逐次尝试会有点累。这时可以写一个不到二十行的小脚本把2到20的所有栏数都跑一遍把所有候选明文列在屏幕上再让眼睛快速扫描哪一行读起来像人话。这种做法在第六节会给出代码。5. 手搓时最容易栽的坑5.1 把空格当成普通字符却不记录前面反复强调过空格是手工栅栏最容易踩的坑。你不妨试一下把“HELLO WORLD”不删空格直接加密R3。字符序列是“HELLO WORLD”长度11包含一个空格。轨道0、1、2会把这个空格当成普通字符处理并在轨道中占位。如果你在解密时不知道空格曾经出现的位置最终能恢复出“HELLOWORLD”却不知道空格应该在单词中间等于没有完全还原原文。我的建议是手搓时统一先删空格如果确实需要保留空格就单独用一张便签记空格位置例如“原文第5个字符后有一个空格”。这个规则要提前约定好发送方和接收方必须一致。5.2 把“3栏”误解成按3个间隔取字符我在不少网友的手记里看到过一种理解3栏就是每隔3个字符抽一次即取位置0、3、6、9组成第一段位置1、4、7组成第二段位置2、5、8组成第三段。这种“窗口取模法”虽然也是一种分组方案但和真正的“之”字形栅栏密码完全是两码事。真正的3栏栅栏密码轨道0取的是位置0、4、8轨道1取的是位置1、3、5、7、9轨道2取的是位置2、6。这种轨道分布来源于“0→1→2→1→0→1→2→1→0→1”的往返路径而不是简单的每隔3个取一个。想判断自己是否理解对了最简单的检验办法是画一次“之”字形路径看字符是否真的落到对应的轨道上。如果画出来的图和“每隔3个取字符”的结果一样说明你把路径画错了。5.3 轨道长度算错导致切片切歪手工解密最常出错的地方就是对“每条轨道到底有几个字符”心里没数。以R3为例你可以用一个简单规律辅助记忆轨道1大约占一半字符轨道0和轨道2各约占四分之一当字符总数是偶数时上下两条轨道数量可能相等或差1当字符总数是奇数时上下两条轨道依然可能相等或差1。但“大约”在精确切分时不够用。我的习惯是先在纸角上写一行“轨道位置表”列出0到4共5个槽位标注哪几个槽位是轨道0、哪几个是轨道1或轨道2然后数一遍每个轨道的次数。多花十秒能避免后面整段解密失败。5.4 字符数太少或最后一段不完整有些短字符串位数不足会导致轨道序列没法完整走一轮比如“TEST”配R3。字符序列T、E、S、T。轨道号按0、1、2、1分配轨道0拿到T和T轨道1拿到E轨道2拿到S密文为“TTES”。解密时轨道0长度2、轨道1长度1、轨道2长度1切分清晰按路径还原得到TEST完全正常。不要因为某条轨道只有一个字符、甚至某些轨道为空就慌乱。轨道为空的情况只会在“栏数大于字符数”时出现比如3个字符分配4栏此时部分轨道空洞加密时密文长度仍然等于明文长度解密时依然按轨道长度切分最后照常还原。5.5 中英文字符计数范围不统一英文里一个字母算一个字符中文里一个汉字也算一个字符。对栅栏密码来说处理粒度统一是手动执行的前提。如果你拿的是混合文本比如“去吃KFC”就需要提前约定规则是把每个汉字和每个英文字母都看作一个独立位置还是按“英文单词整体算一个字符”处理。我的经验是优先按“独立字符”处理也就是“去、吃、K、F、C”共5个字符这样规则清晰且不依赖分词。5.6 加密和解密方向正好搞反有人会在同一张纸上加密完又急着解密结果把“从明文按轨道分发”和“从密文按路径取回”这两个方向记反导致怎么处理都不对。我在自己的草稿纸边角上会写一句提示加密是把字符送进轨道解密是从轨道把字符按原路径领出来。这个提示虽然简单但能救回很多次注意力涣散时的手误。5.7 以为栏数越大越安全如果把栅栏密码当作正经密文来传递重要信息我建议你尽快停止这种想法。栅栏密码的字母频率和明文完全一致攻击者只要判断出这是换位密码再手工尝试几种可能的栏数很快就能破解。它是一种教育价值大于保护价值的古典密码。真正需要安全的时候请选择现代加密算法。栅栏密码更适合用于谜题、游戏、教学和头脑锻炼。5.8 误把“密文分组展示”当成规则很多网上的输出会把密文按4个或5个字符一组展示例如“WECR LTE ERDS OEEF EAOC AIVD EN”。这纯粹是为了方便阅读和抄写不是加密规则的一部分。解密前请把所有分组空格全部去掉恢复成连续字符串“WECRLTEERDSOEEFEAOCAIVDEN”再进行切分。如果你在解密时带着空格算位置轨道分配会全面错位。6. 从手搓到写脚本三个扩展方向6.1 用一段短代码验证手工结果手搓毕竟是手工活偶尔会出错。我在做完长明文后常会用一段短脚本做交叉验证。下面这个Python版本适合加密和解密双方向校验。def rail_fence_encode(s: str, r: int) - str: if r 1: return s rails [[] for _ in range(r)] row, step 0, 1 for ch in s: rails[row].append(ch) if row 0: step 1 elif row r - 1: step -1 row step return .join(.join(rail) for rail in rails) def rail_fence_decode(c: str, r: int) - str: if r 1: return c n len(c) cycle 2 * (r - 1) lens [0] * r for i in range(n): pos i % cycle row pos if pos r else cycle - pos lens[row] 1 idx 0 rails [] for length in lens: rails.append(list(c[idx:idx length])) idx length ptr [0] * r res [] row, step 0, 1 for _ in range(n): res.append(rails[row][ptr[row]]) ptr[row] 1 if row 0: step 1 elif row r - 1: step -1 row step return .join(res) print(rail_fence_encode(HELLOWORLD, 3)) # HOLELWRDLO print(rail_fence_decode(HOLELWRDLO, 3)) # HELLOWORLD print(rail_fence_encode(今晚吃烧烤, 3)) # 今烤晚烧吃 print(rail_fence_decode(今烤晚烧吃, 3)) # 今晚吃烧烤 print(rail_fence_decode( WECRLTEERDSOEEFEAOCAIVDEN, 3 )) # WEAREDISCOVEREDFLEEATONCE用这个脚本去核对前面所有手工例子你会看到输出完全一致。把脚本跑通之后手动爆破栏数就变成一件很轻松的事一个循环遍历R从2到20把每个候选明文打印出来肉眼扫一眼就能找到答案。6.2 把栅栏和替换密码组合起来单独用栅栏密码安全性有限但如果把它和凯撒密码这类替换密码组合效果会立竿见影。一个推荐的组合方式先用栅栏密码换位再用凯撒密码逐字母平移解密时先反向平移再逆向还原栅栏。这种“换位替换”的组合结构正是很多古典密码教材里“多重加密”的入门示范。手搓时虽然多了一步但每步都足够简单组合之后却会令旁观者感到陌生感倍增。6.3 用位置公式实现盲算最后分享一个进阶小技巧让你在不画图的情况下也能算出某个字符在栅栏里的轨道号。以R3为例周期T4。任意位置下标i先计算rem i % (2 * (R - 1))如果rem小于R说明这个字符位于轨道rem如果rem不小于R则它位于轨道(2*(R-1)-rem)。例如R3时位置5rem5%41小于R所以轨道号是1。位置6rem6%42小于R轨道号是2。位置7rem3不小于R轨道号4-31。这个公式在写脚本、做长文本推演、或者画图看不清时特别有用。它本质上就是把“之”字形的折返过程换算成了数学上的“余数折叠”操作。玩过几轮之后你会明显感到栅栏密码的精髓不在一串密文有多“乱”而在于你理解了“字符如何在固定轨道间往返流动”。手搓一遍抵得上阅读十遍资料。下次再看到朋友发来一段读不通的乱序字符串你可以试着画三条线让它在栅栏上跑一遍常常就能把原话接回来。
返回列表