换位加密
换位加密算法是一种较为简单的加密算法,由明文、密钥得出最后的密文,就是通过密钥对明文进行加密,换个位置。
1.栅栏密码
传统型栅栏加密
栅栏密码就是把要加密的明文分成N个一组,然后把每组的第1个字符组合,每组第2个字符组合…每组的第N(最后一个分组可能不足N个)个字符组合,最后把他们全部连接起来就是密文。
案例:
明文:you are goood ctfer
假设栏数为4
分栏:youa rego oodc tfer
去除空格:yrot oeof ugde aocr
密文:yrotoeofugdeaocr
传统型栅栏加密又称直型加密,我们通过上面这张图就可以直观看出来。
解密:
解密传统型栅栏密码除了可以使用常见的工具CTFcrack外,笔者这里也提供给大家一个网上收藏的脚本。
# _*_ encoding:utf-8 _*_
import math
def buwei(encrypted_str,fence_length): # 比如 14,4
str_len = len(encrypted_str)
fence_count = math.ceil(str_len/ fence_length) # 得出4
target_length = fence_count*fence_length
jiequ = []
while str_len<target_length:
encrypted_str = encrypted_str + '*'
jiequ.append(encrypted_str[-fence_count :])
encrypted_str = encrypted_str[:-fence_count]
str_len += 1
jiequ.reverse()
s = ''
for i in jiequ:
s = s + i
result = encrypted_str + s
return result
def decrypt_fence(encrypted_str,fence_length):
encrypted_str = buwei(encrypted_str,fence_length)
if fence_length>=len(encrypted_str) or fence_length<1:
print("栅栏长度太大或者太小,无需解密")
return
fence_count = math.ceil(len(encrypted_str)/fence_length)
elen=len(encrypted_str)
# b = elen // f # 用字符串实际长度除以上面计算出能整出的数字f
result = {x: '' for x in range(fence_count)}
for i in range(elen): # 字符串有多少位,就循环多少次
a = i % fence_count
result.update({a: result[a] + encrypted_str[i]}) # 字符串截断,并更新数据
d = ''
for i in range(len(result)):
d += result[i]
d = d.replace("*", '')
print(f'假设每栏字数为:{fence_length},解密结果为:{d}') # 输出结果,并开始下一个循环
for i in range(1,48): #通过修改48 设定最高解密的栏数。
decrypt_fence('yrotoeofugdeaocr', i) #此处填写要解密的密文
这个脚本的好处是可以设置最高的解密栏数,从而遍历出最终结果。
W型栅栏加密
W型栅栏密码加密的方法中,明文由上至下顺序写上,当到达最低部时,再回头向上,一直重复直至整篇明文写完为止。此例子中,包含了四栏及一段明文:flag{wzhalanwzhalan}。如下:
['f', '.', '.', '.', '.', '.', 'z', '.', '.', '.', '.', '.', 'w', '.', '.', '.', '.', '.', 'n', '.']
['.', 'l', '.', '.', '.', 'w', '.', 'h', '.', '.', '.', 'n', '.', 'z', '.', '.', '.', 'a', '.', '}']
['.', '.', 'a', '.', '{', '.', '.', '.', 'a', '.', 'a', '.', '.', '.', 'h', '.', 'l', '.', '.', '.']
['.', '.', '.', 'g', '.', '.', '.', '.', '.', 'l', '.', '.', '.', '.', '.', 'a', '.', '.', '.', '.']
我们按行读取后密文:fzwnlwhnza}a{aahlgla
W型的加密密钥就不只能是字符串长度的因子,小于其长度的任何一个数都可能是其key值,所以第一步也是确定密钥,也就是栏数。
解密:
在这里也分享一个笔者在网上收藏的脚本:
# -*- coding: utf-8 -*-
def enc(plain, num):
matrix = [([0] * len(plain)) for i in range(num)]
# 获取i的取值序列
i_s = []
for a in range(num):
i_s.append(a)
for a in range(num - 2, 0, -1):
i_s.append(a)
i_s_len = len(i_s)
# 按规则写入
i = 0
for c in plain:
matrix[i_s[i % i_s_len]][i] = c
i += 1
# 排除空值,从头到尾取出
encrypted = ''
for i in range(num):
for j in range(len(plain)):
if matrix[i][j]:
encrypted += matrix[i][j]
# 临时输出
# for i in range(num):
# for j in range(len(plain)):
# print (matrix[i][j], ' ')
# print()
return encrypted
def dec(encrypted, num):
matrix = [([0] * len(encrypted)) for i in range(num)]
cur = 0
for i in range(num): # 按行来填
# 生成每行空格个数的取值序列
if i == 0: # 第1行和最后一行,只需要一个取值就好了
pair = [(num - (i + 1)) * 2 - 1]
elif i == num - 1:
pair = [i * 2 - 1]
else:
pair = [(num - (i + 1)) * 2 - 1, i * 2 - 1]
# 按规则填入
pair_i = 0
j = i
while True:
if cur < len(encrypted):
matrix[i][j] = encrypted[cur]
cur += 1
j += pair[pair_i % len(pair)] + 1 # 这里要加1,直接加间隔是不够的
pair_i += 1
if j >= len(encrypted):
break
# 临时输出
# for i in range(num):
# for j in range(len(encrypted)):
# print (matrix[i][j], ' ')
# print()
# 获取i的取值序列
i_s = []
for a in range(num):
i_s.append(a)
for a in range(num - 2, 0, -1):
i_s.append(a)
i_s_len = len(i_s)
# 按规则取出
decrypted = ''
for j in range(len(encrypted)):
decrypted += matrix[i_s[j % i_s_len]][j]
return decrypted
encrypted = 'fzwnlwhnza}a{aahlgla'
num = 5
for i in range(2, len(encrypted)):
print('分为' + str(i) + '栏时,解密结果为:' + dec(encrypted, i))
'''
readme:
encrypted = 'fzwnlwhnza}a{aahlgla'中修改自己要解密的密文.
脚本会自动识别字符串个数,列出所有栏数的可能
'''
这个脚本的好处就是会自动识别字符串个数,列出所有栏数的可能。得到最终结果。
2.曲路密码
曲路密码也是换位密码的一种,需要加密方与解密方约定加解密路径(也就是曲路路径)。
下面我来举个例子:
我是加密方A,我想要发一段文字给解密方B
未加密文段为:Welcome to the DedSec
然后按照事先约定的序列填入3X6的列表里
加密方与解密方互相约定加密路线
这样加密的密文:cem ohe stc lod ete wed
解密:
这里分享给大家一个曲路密码的解密脚本,不过是c语言。而且这个脚本适用的曲路路径也是上图所示的路径。
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
string encrypt(const string &text, const int &rows, const int &cols) {//加密算法
int length = text.length();
if (length != rows * cols)
return "wrong matrix";//保证矩阵matrix恰好装下text
string matrix[rows];
for (int i = 0; i < rows; ++i) {
matrix[i] = text.substr(i * cols, cols);
}
//矩阵计算完毕,开始曲路取字符
string encrypted;
int x = cols - 1;
int y = rows - 1; //当前x,y坐标位置
int director = -1;//竖直方向上有向下和向上两种方向,需要一个director变量记录方向
bool turn_flag = 0;//拐弯标记
for (int i = 0; i < length; i++) {
encrypted += matrix[y][x];
if (y == 0 || y == rows - 1) {//y=0或者y=rows-1时需要拐弯
if (turn_flag == 0) {
//考虑拐弯有两种,一种是竖直方向转向水平,一种是水平方向转向数值,需要分类讨论
//但是两种转向方式是交替出现的,因此用一个只有两个状态的开关trun_flag来记录区别就可以
turn_flag = 1;//如果本次是水平方向转向竖直方向,那么下一次一定是从竖直方向转向水平方向
y += director;//此种情况是水平方向转向竖直方向,需要在竖直方向上向director方向迈出一步
} else {
turn_flag = 0;
--x;//此种情况是竖直方向转水平方向,需要水平向左迈出一步
}
if (y == 0 )
director = 1;//如果y处在最高处,那么以后的竖直方向一定是下降的
else if (y == rows - 1)
director = -1;//如果y处在最低处,那么以后的竖直方向一定是上升的
} else
y += director;//此处else判断的是在竖直半路上位置,不需要考虑x的变化,只需要无脑听从director的指示
}
return encrypted;
}
string decrypt(const string &encrypted, const int &rows, const int &cols) {
int length = encrypted.length();
if (length != rows * cols)
return "wrong matrix";
string matrix[rows];
for (int i = 0; i < rows; i++) {
matrix[i].resize(cols);
}
int x = cols - 1;
int y = rows - 1; //当前x,y坐标位置
int director = -1;
int turn_flag = 0;//拐弯标记
for (int i = 0; i < length; i++) {
matrix[y][x] = encrypted[i];
if (y == 0 || y == rows - 1) {//y=0或者y=rows-1时需要拐弯
if (turn_flag == 0) {
turn_flag = 1;
y += director;
} else {
turn_flag = 0;
--x;
}
if (y == 0 )
director = 1;
else if (y == rows - 1)
director = -1;
} else
y += director;
}
string text;
for (int i = 0; i < rows; i++) {
text += matrix[i];
}
return text;
}
int main() {
string text = "WelcometotheDedSec";
string encrypted = encrypt(text, 3, 6);
string decrypted = decrypt(encrypted, 3, 6);
cout << encrypted << endl;
cout << decrypted << endl;
return 0;
}
最终得到结果WelcometotheDedSec。
替换加密
替换加密法,是一种用一个字符替换另一个字符的加密方法。
而根据偏移量的不同,还存在若干特定的恺撒密码名称:
偏移量为10: Avocat(A→K)
偏移量为13:ROT13
偏移量为-5: Cassis (K6)
偏移量为-6: Cassette (K7)
由此可知我们要解密一段恺撒加密后的密文,最重要的就是确定偏移量。
下面我们解密这段密文:iodj{YHQLYLGLYLFL}
解密:
这里我们直接使用工具CTFcrack解密即可。
2.ROT13
rot13是恺撒密码的一种变体。
rot13是自身的逆反。即:要还原成原文只要使用同一算法即可得,故同样的操作可用于加密与解密。该算法并没有提供真正密码学上的保全,故它不应该被用于需要保全的用途上。它常常被当作弱加密示例的典型。
案例:
再加密一次你就得到flag啦~
synt{IRAVIVQVIVPV}
解密:
#coding:utf-8
#python2
import string
def decoder(crypt_str,shift):
crypt_list = list(crypt_str)
plain_str = ""
num = int(shift)
for ch in crypt_list:
ch = ord(ch)
if ord('a') <= ch and ch <= ord('z'):
ch = ch + num
if ch > ord('z'):
ch -= 26
if ord('A') <= ch and ch <= ord('Z'):
ch = ch +num
if ch > ord('Z'):
ch -= 26
a=chr(ch)
plain_str += a
print(plain_str)
crypt_str = raw_input("Crypto_text:")
print "!------decode------!"
shift=13 #修改数字确定位移量。选择13 则为ROT13加密 ROT13是它自身的逆反,即:要还原成原文只要使用同一算法即可得,故同样的操作可用于加密与解密。
decoder(crypt_str,shift)
最终得到flag{VENIVIDIVICI}。
3.猪圈密码
比起叫猪圈密码,笔者更喜欢叫他共济会密码。是一种以格子为基础的简单替代式密码。即使使用符号,也不会影响密码分析,亦可用在其它替代式的方法。
下面的图片就是猪圈密码与26个字母的对应表。
如果不方便理解可以结合猪圈密码设计图。
解密:
解密的话目前没有脚本,可以通过在线解密网站进行解密。当无法联网时需要通过对照图进行解密。
猪圈密码在线转换:http://moersima.00cha.net/zhuquan.asp
4.培根密码
培根密码(Baconian Cipher)是一种替换密码,每个明文字母被一个由5字符组成的序列替换。
原理:最初的加密方式就是由‘A’和‘B’组成序列替换明文,比如字母‘D’替换成“aaabb”。
大家注意俩个映射表的区别不止大小写。大小写在培根密码中不区分。我们注意第二个映射表i和j,u和v对应的是同一个密码。从映射表我们也可以知道培根密码只对字母进行加密。
案例:
AAAAB AAAAA AAABA ABBBA ABBAB ABAAA BAABA AAAAA ABBAB AABBA BAAAB BBAAA
解密:
这个脚本,算是我从网上收藏的一个脚本,可以加密也可以解密,而且俩种映射表都会有。
# coding:utf8
# python2.X
import re
alphabet = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z']
first_cipher = ["aaaaa","aaaab","aaaba","aaabb","aabaa","aabab","aabba","aabbb","abaaa","abaab","ababa","ababb","abbaa","abbab","abbba","abbbb","baaaa","baaab","baaba","baabb","babaa","babab","babba","babbb","bbaaa","bbaab"]
second_cipher = ["aaaaa","aaaab","aaaba","aaabb","aabaa","aabab","aabba","aabbb","abaaa","abaaa","abaab","ababa","ababb","abbaa","abbab","abbba","abbbb","baaaa","baaab","baaba","baabb","baabb","babaa","babab","babba","babbb"]
def encode():
upper_flag = False # 用于判断输入是否为大写
string = raw_input("please input string to encode:n")
if string.isupper():
upper_flag = True
string = string.lower()
e_string1 = ""
e_string2 = ""
for index in string:
for i in range(0,26):
if index == alphabet[i]:
e_string1 += first_cipher[i]
e_string2 += second_cipher[i]
break
if upper_flag:
e_string1 = e_string1.upper()
e_string2 = e_string2.upper()
print "first encode method result is:n"+e_string1
print "second encode method result is:n"+e_string2
return
def decode():
upper_flag = False # 用于判断输入是否为大写
e_string = raw_input("please input string to decode:n")
if e_string.isupper():
upper_flag = True
e_string = e_string.lower()
e_array = re.findall(".{5}",e_string)
d_string1 = ""
d_string2 = ""
for index in e_array:
for i in range(0,26):
if index == first_cipher[i]:
d_string1 += alphabet[i]
if index == second_cipher[i]:
d_string2 += alphabet[i]
if upper_flag:
d_string1 = d_string1.upper()
d_string2 = d_string2.upper()
print "first decode method result is:n"+d_string1
print "second decode method result is:n"+d_string2
return
if __name__ == '__main__':
print "ttcoding by qux"
while True:
print "t*******Bacon Encode_Decode System*******"
print "input should be only lowercase or uppercase,cipher just include a,b(or A,B)"
print "1.encoden2.decode(解密的密文需要去掉空格)n3.exit"
s_number = raw_input("please input number to choosen")
if s_number == "1":
encode()
raw_input()
elif s_number == "2":
decode()
raw_input()
elif s_number == "3":
break
else:
continue
参考链接:
https://www.bilibili.com/read/cv6496295
原文始发于微信公众号(小草培养创研中心):技术流丨古典密码学梳理及解密脚本(一)
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论