密码和编码都是加密,但是有着本质区别:那个密码比编码多一个密钥(key)。这里的密码,不是指平常用来登陆奇安信的密码,而是指加密字符串。我们将数据(明文)通过一定规则(秘钥)进行打乱混编(加密)得到字符串(密文),这就是密码的基本流程。密码学中,一般将明文用m表示,将密文用c表示,将秘钥k表示。
移位密码是最简单的密码形式之一,也是最容易理解的密码形式。上述加密基本流程就是针对这种密码形式的最大白话的描述。
这里有一个明文“qianxinshequ”,有一个秘钥“4132”,用下面的形式进行书写:
m="qianxinshequ"
k="4132"
当m为qianxinshequ的时候,先按照秘钥长度对其进行分割,已知 len(k)=4 :
qian xins hequ
m被分为了3部分,按照k的数字顺序对每一部分的明文进行移位加密。根据k,每一部分的明文第一位被换到了第四位,第二位被换到了第一位,第三位依旧是第三位,第四位被换到第二位。剩下的两部分,以此类推。
经过变化,m变为:
inaq isnx euqh
合并就变成了密文:
inaqisnxeuqh
一个简单移位密码完成。
将m填入一个表中,按照定向的连贯的顺序进行遍历,对其进行加密,是移位密码的一种。
此时,k看起来是一个表的形式,但仍然可以用字符串进行表达。因为k就是行列数。如下图所示:修改行列数就可以修改k。
有以下m
m = "qianxinyuanchuang"
k为7行5列,c为:
unihcxnngaaiunayq
加密程序代码:
import re
col = 7
row = 5
m = input()
c = ""
temp = []for i in range(col):
temp.append([])for index, i in enumerate(m):
temp[index % col].append(i)
re_temp = list(reversed(temp))for index, i in enumerate(re_temp):
if index % 2 == 0:
i = list(reversed(i))
c += "".join(i)
plaintext = ""
length = len(m)
min_row = length // col
min_num = col - length % col
temp = []
index = 0for i in range(col):
if i < min_num:
temp.append(m[index:index+min_row])
index += min_row
else:
temp.append(m[index:index+min_row+1])
index += min_row + 1print(temp)for index, i in enumerate(temp):
if index % 2 == 0:
temp[index] = "".join(list(reversed(re.findall(".{1}", temp[index]))))
temp.reverse()for i in range(length):
plaintext += temp[i % col][i // col]print(f"{plaintext} : {c}")
栅栏密码是一种规则比较特殊的移位密码,他的k是一个数字,用来表示栅栏的长度。其具体加密过程为,将要加密的m分成若干组。每组一共有k个字符。然后取每组第一个字符顺次连接,组成第一个字符串。然后再将第二个字符顺次连接,组成第二个字符串。以此类推,直到所有的m加密完毕。最后将加密后的字符串拼接在一起,便是c。
有以下样例
m = "qianxin"
k = "2"
按照k进行分解
qi an xi n
每组首位相加,以此类推
qaxn ini
合并得出c
c = "qaxnini"
加密程序代码:
m = input()
k = 2
c = []
length = len(m)for i in range(k):
for j in range(i, length, k):
c.append(m[j])print (''.join(c))
解密程序代码:
from calendar import c
from msvcrt import kbhit
c = input()
k = 2
m = []
length = len(c)
q, r = divmod(length, k)
n = (q + 1) if r else q
mid = n * (k - r)for i in range(n - 1):
for j in range(i, mid, n):
m.append(c[j])
for j in range(mid + i, length, n - 1):
m.append(c[j])for j in range(n - 1, mid, n):
m.append(c[j])print (''.join(m))
云影密码,他仅包含01248五个数字,其中的0用于分割,其余数字用于加和操作之后转换为m。
m只包含字母(不区分大小写),将字母在字母表的排位分解为若干数字(只包含1248这四个数字)之和,然后将这些数字排列在一起便是一个字母的c。若干字母的c之间用0作分割线。
例如
m = "QIANXIN"
加密之后就是
c = "88108101084208880810842"
解密是这样的
881 81 1 842 888 81 842
各组相加
17 9 1 14 24 9 14
对应
Q I A N X I N
连起来就是m
m = "QIANXIN"
加密程序:
mcode = input()
dic = [chr(i) for i in range(ord("A"), ord("Z") + 1)]
m = [i for i in mcode]
tmp = [];flag = []for i in range(len(m)):
for j in range(len(dic)):
if m[i] == dic[j]:
tmp.append(j + 1)for i in tmp:
res = ""
if i >= 8:
res += int(i/8)*"8"
if i%8 >=4:
res += int(i%8/4)*"4"
if i%4 >=2:
res += int(i%4/2)*"2"
if i%2 >= 1:
res += int(i%2/1)*"1"
flag.append(res + "0")print ("".join(flag)[:-1])
解密程序:
c = input ()
c = c.split("0")
m = ''for i in range(0, len(c)):
str = c[i]
sum = 0
for i in str:
sum += int(i)
m += chr(sum + 64)print(m)
古典密码当中的移位密码在ctf比赛当中最主要出现以上三种形式,其中的k有的是固定的,有的是自定义的(需要爆破)。
固定的是云影密码,自定义的是曲路密码和栅栏密码。其中曲路密码的k的爆破主要是针对行列数的改变和遍历方向的不同。而栅栏密码爆破则主要针对的是k的数值不同。
竞赛当中也有一些小技巧可以使用。
比如已知m为flag{…},那么如果在c可以主要寻找flag不同的组合方式。通过四个字母的组合方式去推断k的可能性。