目录
前言
最近在学习CTF的密码题目,边做题,边学习用python实现。
提示:全文的明文为IAMMANBUTLIKEWOMEN
在线栅栏密码加密解密,推荐使用这个在线工具,ctf在线工具库里面的,好像有问题。
https://www.qqxiuzi.cn/bianma/zhalanmima.php
一、栅栏密码是什么?
所谓栅栏密码,就是把要加密的明文分成N个一组,然后把每组的第1个字连起来,形成一段无规律的话。 不过栅栏密码本身有一个潜规则,就是组成栅栏的字母一般不会太多。(一般不超过30个,也就是一、两句话) from 百度百科
二、使用步骤
1.加密过程理解+实现
明文为IAMMANBUTLIKEWOMEN ;加密密钥为3
加密阵列见表
m\n | 0列 | 1列 | 2列 |
分组0 | I (0) | A (1) | M (2) |
分组1 | M (3) | A (4) | N (5) |
分组2 | B (6) | U (7) | T (8) |
分组3 | L (9) | I (10) | K (11) |
分组4 | E (12) | W (13) | O (14) |
分组5 | M (15) | E (16) | N (17) |
则:密文为IMBLEM AAUIWE MNTKON
解读:将一段字符串(明文)按照(加密密钥)3个一组进行排列,然后将每组的第一列提取,第二列,第三列。分别循环提取。
第0列,下标依次是(0、3、6、9、12、15) +0
第1列,下标依次是(1、4、7、10、13、16)看作(0、3、6、9、12、15)+1
第2列,下标依次是(2、5、8、11、14、17)看作(0、3、6、9、12、15)+2
小结:在加密过程中,每次提取string_C明文串中 下标为 [m*Ek+n] 的字符
m为 加密过程中得 第m分组
取值为0,1,2, ……,len(string_C)/Ek -1
(上例明文长度为18,加密密钥为3,则相除 得6;m=0,1,2,3,4,5)
注意,当 明文长度 不能被 密钥 整除时,会多出一个分组。
如果长度为18 ,密钥为5,相除得3.6。
这时m得取值为0,1,2, ……,int(len(string_C)/Ek)
但在代码实现过程中,不需要特别区分。 m < len(string_C)/Ek 即可
n为 加密过程中,第n列字符,取值为0,1,2, …… ,Ek-1 (上例n= 0,1,2)
n < Ek
加密函数:
def fun_enCrypto(string_C , Ek):
string_M = '' #初始化密文
n = 0 #提取第n列字符
while n < Ek:
m = 0 #第m分组
while m < len(string_C) / Ek: #明文长度/加密密钥 即得分组个数
if (m * Ek+ n) < len(string_C):
string_M = string_M + string_C[int(m * Ek+ n)]
m += 1
else :
break
n += 1
return string_M
2.解密过程理解+实现
密文为IMBLEMAAUIWEMNTKON
加密密钥为3
求明文,解密密钥为Dk=len(string_M)/Ek 为18/3=6
按照解密规则,得出如下过程。
m\n | 0列 | 1列 | 2列 | 3列 | 4列 | 5列 |
分组0 | I(0) | M(1) | B(2) | L(3) | E(4) | M(5) |
分组1 | A(6) | A(7) | U(8) | I(9) | W(10) | E(11) |
分组2 | M(12) | N(13) | T(14) | K(15) | O(16) | N(17) |
明文:IAM MAN BUT LIK EWO MEN
解读:将一段字符串(密文)按照6(解密密钥)个一组进行排列,然后将每组的第一列提取,第二列,第三列,第四列,第五列,第六列。分别循环提取。
0列,第一列下标依次是(0、6、12) +0
1列,第二列下标依次是(1、7、13)看作(0、6、12)+1
*
n列,第n+1列下标依次是(0*Dk+n,1*Dk+n,2*Dk+n)
*
5列,第六列下标依次是(5、11、17)看作(0、6、12)+5
小结:在解密过程中,每次提取string_M密文串中 下标为 [m*Dk+n] 的字符
m为 解密过程中得 第m分组
取值为0,1,2, ……,len(string_M)/Dk -1
(在上例中18/6=3;m=0,1,2);Dk由len(string_M)/Ek得到。
解密过程代码实现:
n = 0
while n < Dk:
m = 0
while m < Ek:
if (m * Dk + n) < len(string_M):
string_C = string_C + string_M[int(m * Dk + n)]
m += 1
else:
break
n += 1
return string_C
3.解密过程问题:
用上述代码实现解密的时候,运行程序时,发生问题:
如:已知字符串长度为18,当Ek=5时,解密得到明文就不是我们希望得到的明文。发现,当len(string_M)% Ek != 0 时,上述过程返回明文字符串不正确。
结论:再代码实现时,需判断 len(string_M)% Ek 是否 等于 0
我们对一个余数≠0的实例进行解读:(加密密钥Ek为5;密文:INIMABKEMUENMTWALO)
解密阵列:INIMABKEMUENMTWALO
求明文,解密密钥为Dk=len(string_M)/Ek 为18=Ek*3+3 (18/15=3.5)
0列 | 1列 | 2列 | 3列 | |
分组0 | I(0) | N(1) | I(2) | M(3) |
分组1 | A(4) | B(5) | K(6) | E(7) |
分组2 | M(8) | U(9) | E(10) | N(11) |
分组3 | M(12) | T(13) | W(14) | |
分组4 | A(15) | L(16) | O(17) |
通过观察解密阵列,无法通过m\n进行提取字符。
这里引入一个概念:步长数组。即解密过程中下一个字符到上一个字符的下标之差组合成一个数组,上例中,步长数组位[4,4,4,3,3] 。数组元素个数为Ek(加密密钥)
1.若len(M)%Ek==0时,Dk=len(M)/Ek,步长=Dk。 2.若len(M)%Ek≠0 时,则步长存在大步长=int(Dk)+1和小布长Dk。相差1. 大步长的个数为 len(M)%Ek 余数。 小布长的个数为 Ek-len(M)%Ek
#Rail-Fence Cipher栅栏解密,输入加密分组中每组中的字符个数
def fun_deCrypto(string_M, Ek):
Dk = int(len(string_M) / Ek)
string_C = ''
yushu = len(string_M) % Ek
steps = []
if len(string_M) % Ek == 0:
print('不存在余数')
step = Dk
for i in range(Ek):
steps.append(step)
print(steps)
else:
print('存在余数')
big_step = math.ceil(len(string_M) / Ek)
small_step = int(len(string_M) / Ek)
for p in range(yushu):
steps.append(big_step)
for q in range(Ek - yushu):
steps.append(small_step)
print(steps)
n_column = 0
while n_column < math.ceil(len(string_M) / Ek):
count_steps = 0
for one_step in steps:
if len(string_C) == len(string_M):
break
else:
string_C += string_M[n_column + count_steps]
count_steps += one_step
n_column += 1
return string_C
总结
栅栏加密解密,即将一个字符串。按照一定的间隔提取单个字符,再按照提取的先后顺序,重组成一个字符串。
加解密都可以通过步长数组实现。如果步长数组为[2,2,1,1],首先提取起始0位,走2步,到第2位,再走2步,到第4位,再走1步到第5位。起始位+1,提取第1位,再走2步,到第3位。