维吉尼亚表和列置换结合加密

维吉尼亚表和列置换结合加密

功能概述

  1. 该加密算法结合采用了维吉尼亚(Vigenere)表的一次密码本加密以及列置换加密方案。
  2. 密钥为一个随机密码本中的一个随机段落。

    加密算法

  3. 加密时,明文与密钥key由用户输入。密钥可以是任意的一个随机密码本中的一个随机段落。程序将明文与密钥存放于两个列表之中。一般来说密钥的长度小于明文的长度,程序将会把密钥重复,直至两个列表长度相等,以达到两个列表中的元素能够一一对应的目的。明文与密钥对应相加,再模26,得到密文对应的数字,再转换为对应字母。随后进行列置换再进行一次加密。

    #加密函数
    def Encrypt(P,key):
    P=list(P)
    for i in range(len(P)):
    P[i]=p[i].lower()
    P=[ord(x)-ord('a') for x in P]
    P=np.array(P)
    m=len(P)//len(key)
    n=len(P)%len(key)
    K=key*m+key[0:n]
    C=(P+K)%26
    C=[chr(x+ord('A')) for x in C]
    C=Encrypt_Transposition(C)
    C=''.join(C)
    return C
  4. 列置换加密即是将上述加密得到的密文横着读,但是以一定长度竖着写出来。(本程序中选择的长度为5.)然后再横着一行一行的写在一起得到一条完整的密文。

    #加密列置换
    def Encrypt_Transposition(B):
    B=list(B)
    A=[]
    D=[]
    a=len(B)
    for i in range(a):
    if (i%5==0 and i!=0):
    A.append(D)
    D=[]
    D.append(B[i])
    if(len(D)!=0):
    A.append(D)
    D=[]
    for i in range(5):
    for j in range(len(A)):
    if ((len(A[j])-1)>=i):
    D.append(A[j][i])
    return D

解密算法

  1. 解密的难度在于将列置换后的密文如何还原为最初只是进行维吉尼亚表加密后得到的密文。如上所述,我们加密之后得到的是一条很长的字符串,它是被横着读竖着写再横着读连成的一条,所以要想解密,我们便反过来将它先横着写,然后竖着读,再横着写成一条即可。
  2. 用户输入需要解密的密文之后,我们将这个字符串强制类型转换存进一个列表里,每5个是一列,那么我们最终只会有5行。那么每一行要写多少个呢?我们是不知道的,密文的长度不一定刚好是5的整数倍,这必然导致每一行的字符数不一定相等。我们用len()函数得到列表的长度之后,将该长度对5作取余运算,假设得到的余数为n。那么便是有n行会比剩余的5-n行多一个字符。此时只需在程序中加入一个计数器,当计数器大小等于n时,每行写的字符串开始比上面的行少一个即可。

    #解密列置换
    def Decrypt_Transposition(B):
    B=list(B)
    A=[]
    D=[]
    b=len(B)
    m=b//5
    n=b%5
    count=0
    for i in range(n):
    for j in range(m+1):
    D.append(B[count])
    count=count+1
    A.append(D)
    D=[]
    for i in range(5-n):
    for j in range(m):
    D.append(B[count])
    count=count+1
    A.append(D)
    D=[]
    D=[]
    for i in range(5):
    for j in range(len(A)):
    if ((len(A[j])-1)>=i):
    D.append(A[j][i])
    return D
  3. 对列置换解密之后,得到的便是最初的密文。我们反向使用维吉尼亚表加密的方法,便可得到明文。

    #解密函数
    def Decrypt(C,key):

    C=list(C)
    C=Decrypt_Transposition(C)
    C=[ord(x)-ord('A') for x in C]
    C=np.array(C)
    m=len(C)//len(key)
    n=len(C)%len(key)
    K=key*m+key[0:n]
    P=(C-K)%26
    P=[chr(x+ord('a')) for x in P]
    P=''.join(P)
    return P

功能实现的完整代码

import numpy as np
#加密列置换
def Encrypt_Transposition(B):
B=list(B)
A=[]
D=[]
a=len(B)
for i in range(a):
if (i%5==0 and i!=0):
A.append(D)
D=[]
D.append(B[i])
if(len(D)!=0):
A.append(D)
D=[]
for i in range(5):
for j in range(len(A)):
if ((len(A[j])-1)>=i):
D.append(A[j][i])
return D
#解密列置换
def Decrypt_Transposition(B):
B=list(B)
A=[]
D=[]
b=len(B)
m=b//5
n=b%5
count=0
for i in range(n):
for j in range(m+1):
D.append(B[count])
count=count+1
A.append(D)
D=[]
for i in range(5-n):
for j in range(m):
D.append(B[count])
count=count+1
A.append(D)
D=[]
D=[]
for i in range(5):
for j in range(len(A)):
if ((len(A[j])-1)>=i):
D.append(A[j][i])
return D


#加密函数
def Encrypt(P,key):
P=list(P)
for i in range(len(P)):
P[i]=p[i].lower()
P=[ord(x)-ord('a') for x in P]
P=np.array(P)
m=len(P)//len(key)
n=len(P)%len(key)
K=key*m+key[0:n]
C=(P+K)%26
C=[chr(x+ord('A')) for x in C]
C=Encrypt_Transposition(C)
C=''.join(C)
return C

#解密函数
def Decrypt(C,key):

C=list(C)
C=Decrypt_Transposition(C)
C=[ord(x)-ord('A') for x in C]
C=np.array(C)
m=len(C)//len(key)
n=len(C)%len(key)
K=key*m+key[0:n]
P=(C-K)%26
P=[chr(x+ord('a')) for x in P]
P=''.join(P)
return P


#主函数
#加密请按E,解密请按D
user_input=input('E or D:');
#输入合法性判断
while(user_input!='E' and user_input!='D'):
user_input=input('Input error, please reenter:')

key=input('Please enter the key:')
while(not key.isalpha()):#输入合法性判断
key=input('Input error,key is alpha, please reenter:')

key=list(key)
for i in range(len(key)):
if key[i].islower():
key[i]=ord(key[i])-ord('a')
else:
key[i]=ord(key[i])-ord('A')

if user_input=='E':
#加密过程
P=input('Please enter a clear text:')
C=Encrypt(P,key)
print ('Ciphertext:%s'% C)
else:
#解密过程
C=input('Please enter a Ciphertext:')
P=Decrypt(C,key)
print ('clear text:%s' % P)

运行环境

python3.6.6及其自带IDLE

测试

随机测试了密钥比明文短和密钥比明文长两种情况

打包

至此,这个程序基本上可以用来玩一玩了,我将它打包成了exe文件,哪怕没有python运行环境的电脑也能使用。


但是在执行exe文件的时候我发现密文输出的一瞬间文件就执行结束了,然后我才想起来我的主函数部分只是执行了一次。于是乎我将主函数部分改了一下,添加了一个while循环,在用户输入done的时候结束循环,表示执行结束,便解决了这个问题。

 
#主函数
while True:
#加密请按E,解密请按D
user_input=input('E or D or done:');
if user_input=='done':
break
#输入合法性判断
while(user_input!='E' and user_input!='D'):
user_input=input('Input error, please reenter:')

key=input('Please enter the key:')
while(not key.isalpha()):#输入合法性判断
key=input('Input error,key is alpha, please reenter:')

key=list(key)
for i in range(len(key)):
if key[i].islower():
key[i]=ord(key[i])-ord('a')
else:
key[i]=ord(key[i])-ord('A')

if user_input=='E':
#加密过程
P=input('Please enter a clear text:')
C=Encrypt(P,key)
print ('Ciphertext:%s'% C)
else:
#解密过程
C=input('Please enter a Ciphertext:')
P=Decrypt(C,key)
print ('clear text:%s' % P)

exe文件测试

等待它跳出输入提示之后再开始

测试样例

图形化界面

  1. 用PyQt5做了图形化界面
  2. 做了图形化界面之后,对原代码也进行了一些修改。加密解密算法没更改,添加了随机生成的密钥,而不是原来的人工输入密钥。
    # 随机生成密钥
    def get_random():
    return ''.join(random.choice(string.ascii_letters) for _ in range(15))

另外,本程序将所有除了字母以外的字符视为了不合法输入,原来的手动输入密钥进行了输入合法性的判断:

key=input('Please enter the key:')  
while(not key.isalpha()):#输入合法性判断
key=input('Input error,key is alpha, please reenter:')

故而随机生成的密钥中也只包含了字母(大小写随机)

  1. 同样,若用户需要在明文密文中使用数字,涉及到数字时可用其英文表示。加密时无论是大写还是小写,本程序在加密函数中将其统一转换为了小写字母,因为大小写不改变英文单词本身含义,且小写更便于人们阅读。故而最终解密得到的也是小写字母。

    #加密函数
    def Encrypt(P,key):
    P=list(P)
    for i in range(len(P)):
    P[i]=p[i].lower()
  2. 使用说明
    运行打包好的exe文件,些许时刻之后会出现以下界面:


第一步应该生成密钥,也可以先输入需要加密的文本再生成密钥。
第二步点击加密,加密后的结果会出现在左下角。
第三步不需要重新生成密钥,直接将左下角出现的密文填入解密框,点击解密即可。

代码

更改后的代码将加密部分和主函数部分分成了两个.py文件,加密解密部分存放于vigenere.py中,主函数部分实现图形化界面以及调用加密解密函数,存放于main.py中。这时候我用的就是PyCharm了而不是python自带的IDLE。

# vigenere.py
import numpy as np
import random, string

# 随机生成密钥
def get_random():
return ''.join(random.choice(string.ascii_letters) for _ in range(15))

# 加密列置换


def Encrypt_Transposition(B):
B = list(B)
A = []
D = []
a = len(B)
for i in range(a):
if (i % 5 == 0 and i != 0):
A.append(D)
D = []
D.append(B[i])
if (len(D) != 0):
A.append(D)
D = []
for i in range(5):
for j in range(len(A)):
if ((len(A[j]) - 1) >= i):
D.append(A[j][i])
return D


# 解密列置换
def Decrypt_Transposition(B):
B = list(B)
A = []
D = []
b = len(B)
m = b // 5
n = b % 5
count = 0
for i in range(n):
for j in range(m + 1):
D.append(B[count])
count = count + 1
A.append(D)
D = []
for i in range(5 - n):
for j in range(m):
D.append(B[count])
count = count + 1
A.append(D)
D = []
D = []
for i in range(5):
for j in range(len(A)):
if ((len(A[j]) - 1) >= i):
D.append(A[j][i])
return D


# 加密函数
def Encrypt(P, key):
P = list(P)
for i in range(len(P)):
P[i] = P[i].lower()
P = [ord(x) - ord('a') for x in P]
P = np.array(P)
m = len(P) // len(key)
n = len(P) % len(key)
K = key * m + key[0:n]
C = (P + K) % 26
C = [chr(x + ord('A')) for x in C]
C = Encrypt_Transposition(C)
C = ''.join(C)
return C


# 解密函数
def Decrypt(C, key):

C = list(C)
C = Decrypt_Transposition(C)
C = [ord(x) - ord('A') for x in C]
C = np.array(C)
m = len(C) // len(key)
n = len(C) % len(key)
K = key * m + key[0:n]
P = (C - K) % 26
P = [chr(x + ord('a')) for x in P]
P = ''.join(P)
return P


# # 主函数
# while True:
# # 加密请按E,解密请按D
# user_input = input('E or D or done:');
# if user_input == 'done':
# break
# # 输入合法性判断
# while (user_input != 'E' and user_input != 'D'):
# user_input = input('Input error, please reenter:')
#
# key = input('Please enter the key:')
# while (not key.isalpha()): # 输入合法性判断
# key = input('Input error,key is alpha, please reenter:')
#
# key = list(key)
# for i in range(len(key)):
# if key[i].islower():
# key[i] = ord(key[i]) - ord('a')
# else:
# key[i] = ord(key[i]) - ord('A')
#
# if user_input == 'E':
# # 加密过程
# P = input('Please enter a clear text:')
# C = Encrypt(P, key)
# print('Ciphertext:%s' % C)
# else:
# # 解密过程
# C = input('Please enter a Ciphertext:')
# P = Decrypt(C, key)
# print('clear text:%s' % P)

# main.py
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
import sys
from vigenere import *
class Main(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle("维吉尼亚表与列置换结合加密")

self.layout = QVBoxLayout(self)
self.enc_area = QTextEdit()
self.dec_area = QTextEdit()
self.enc_area.setPlaceholderText("加密框")
self.dec_area.setPlaceholderText("解密框")
self.row = QHBoxLayout()
self.row.addWidget(self.enc_area)
self.row.addWidget(self.dec_area)
self.layout.addLayout(self.row)

self.row2 = QHBoxLayout()
self.enc_btn = QPushButton("加密")
self.dec_btn = QPushButton("解密")
self.row2.addWidget(self.enc_btn)
self.row2.addWidget(self.dec_btn)
self.layout.addLayout(self.row2)

self.gen_btn = QPushButton("生成密钥")
self.layout.addWidget(self.gen_btn)

self.clear_btn = QPushButton("清空!")
self.layout.addWidget(self.clear_btn)
self.result = QLabel()
self.layout.addWidget(self.result)

self.bind_evt()


def bind_evt(self):
def set_key():
self.key = get_random()
QMessageBox.information(None, "生成密钥", "密钥为%s" % self.key)
self.key = list(self.key)
for i in range(len(self.key)):
if self.key[i].islower():
self.key[i] = ord(self.key[i]) - ord('a')
else:
self.key[i] = ord(self.key[i]) - ord('A')
self.gen_btn.clicked.connect(set_key)

def encode():
# print(self.enc_area.toPlainText(),self.key)
s = Encrypt(self.enc_area.toPlainText(),self.key)
self.result.setText(s)
self.enc_btn.clicked.connect(encode)

def decode():
s = Decrypt(self.dec_area.toPlainText(), self.key)
self.result.setText(s)
self.dec_btn.clicked.connect(decode)

def clear():
self.enc_area.clear()
self.dec_area.clear()
self.result.clear()
self.clear_btn.clicked.connect(clear)


if __name__ == '__main__':
app = QApplication(sys.argv)
m = Main()
m.show()
sys.exit(app.exec())
Author: 其培之也
Link: https://moriarty98.github.io/2019/06/09/维吉尼亚表和列置换结合加密/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.