异或加解密作业

Posted by Fangjuntao on 2022-01-03

前言

作业要求:

1
2
3
4
5
6

选择一门自己熟悉的计算机语言,实现使用异或运算对数据文件或字符串进行加解密。要求如下:

1. 可以对任意类型的数据文件加密,并解密。其中密钥由用户输入,密钥可以使用各种可打印字符。
2. 具有较友好的用户界面,建议使用图形界面。
3. 使用git进行管理,要有足够的提交个数,即每增加一个重要的功能,进行一次提交;修复一个重大的Bug,进行一次提交。

参考: https://blog.csdn.net/ctwy291314/article/details/88820930

https://blog.csdn.net/weixin_42557907/article/details/81605543

异或加解密原理

  1. 异或运算:首先异或表示当两个数用二进制表示,进行异或运算时,当前位的两个二进制不同则为1相同则为0.
    image

  2. python 异或符号为:“ ^ ”

  3. 异或加解密过程如下:

1
2
3
4
5
加密: A  ^ B  = C 

解密:C ^ B = A

其中A为明文 ,B 为密钥 ,C为密文

实现

实现对输入字符串的加解密

加密

这个比较简单,重点在于利用python的ord()函数和 chr()函数 ,以及将复制用户输入的key使其长度与明文的二进制长度相同(我们采用逐个字符对应异或)

  1. ord()函数:
    参考菜鸟教程
1
2
3
4
5
6
7
8

ord() 函数是 chr()

函数(对于8位的ASCII字符串)或 unichr() 函数(对于Unicode对象)的配对函数,

它以一个字符(长度为1的字符串)作为参数,返回对应的 ASCII 数值,或者 Unicode
数值,
如果所给的 Unicode 字符超出了你的 Python 定义范围,则会引发一个 TypeError 的异常。

egg:

1
2
3
4
5
6
7
8


>>>ord('a')
97
>>> ord('b')
98
>>> ord('c')
99
  1. chr() :
    用一个范围在 range(256)内的(就是0~255)整数作参数,返回一个对应的字符。
    菜鸟教程:
1
2
3
4
5
6


>>>print chr(0x30), chr(0x31), chr(0x61) # 十六进制
0 1 a
>>> print chr(48), chr(49), chr(97) # 十进制
0 1 a
  1. 使key 与明文字符长度一致,逐个字符进行异或:
1
2
3
4
5
6
def encode(plaintext,key):
key = key * (len(plaintext) // len(key)) + key[:len(plaintext) % len(key)] #取整数/余数,使得实际key与明文的长度一样
ciphertext=[]
for i in range(len(plaintext)):
ciphertext.append(str(ord(plaintext[i])^ord(key[i])))
return '-'.join(ciphertext) #方便解密是分割

解密

利用加密的特点“-”分割每个字符
其它原来同上,毕竟就是再异或一次

1
2
3
4
5
6
7
8
9

#异或字符串解密
def decode(ciphertext,key):
ciphertext=ciphertext.split('-')
key=key*(len(ciphertext)//len(key))+key[:len(ciphertext)%len(key)]#取整数/余数
plaintext=[]
for i in range(len(ciphertext)):
plaintext.append(chr(int(ciphertext[i])^ord(key[i])))
return ''.join(plaintext)

实现文件的加解密

功能: 输入文件路径, 加解密密钥
,进行加密或者解密

实现思路:
读文件,每一行遍历,通过迭代器逐字符处理,以字节的方式写入新文件
(因为我们无法知道文件的编码方式,故使用了二进制的方式进行读取文件,字节的形式进行写入文件,这样可以兼容)

  1. 文件的读取:
1
2
with open(path, "rb") as  f_read:
with open(newFilePath, "wb") as f_write:

解析:

  • “rb”、“wb”的"b"表示二进制的方式
  • with open()基于上下文会自动帮我们关闭,所以不需要 手动 f.close()

字节的形式进行写入文件,这样可以兼容:

1
2
3
4
5
6
7
#为了防止解密后的文件出现乱码问题,而我们又无法预知编码,所以采用二进制读文件,字节流解密
# 我们采用异或循环解密
for now in f_read: # 通过迭代器逐行访问
for nowByte in now: # 通过迭代器逐字符处理
newByte = nowByte ^ ord(password[count % len(password)])
count += 1
f_write.write(bytes([newByte]))

总结:

本次作业完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138

# -* -coding: UTF-8 -* -
# 功能:异或方式对文件或字符串进行加密和解密
import os
import datetime
import chardet




# 异或字符串加密
def encode(plaintext,key):
key = key * (len(plaintext) // len(key)) + key[:len(plaintext) % len(key)] #取整数/余数,使得实际key与明文的长度一样
ciphertext=[]
for i in range(len(plaintext)):
ciphertext.append(str(ord(plaintext[i])^ord(key[i])))
return '-'.join(ciphertext)


#异或字符串解密
def decode(ciphertext,key):
ciphertext=ciphertext.split('-')
key=key*(len(ciphertext)//len(key))+key[:len(ciphertext)%len(key)]#取整数/余数
plaintext=[]
for i in range(len(ciphertext)):
plaintext.append(chr(int(ciphertext[i])^ord(key[i])))
return ''.join(plaintext)


def encryFile(path, password):
start = datetime.datetime.now()

fileFullName = path.split(os.path.sep) # os.path.sep为操作系统的文件分隔符
fileName = fileFullName[len(fileFullName) - 1].split(".")[0] #获取文件名
fileSuffix = fileFullName[len(fileFullName) - 1].split(".")[1] #获取文件后缀

# print("文件全名称:",fileFullName[len(fileFullName)-1])
# print("文件名称:",fileName)
# print("文件后缀:",fileSuffix)

fileParent = path[0:len(path) - len(fileFullName[len(fileFullName) - 1])]
newFileName = "加密_" + fileFullName[len(fileFullName) - 1]
newFilePath = fileParent + newFileName

# print("文件父路径:",fileParent)
# print("新的文件名称:",newFileName)
# print("新的文件全路径:", newFilePath)

with open(path, "rb") as f_read:
with open(newFilePath, "wb") as f_write:
count = 0
# 我们采用异或循环加密
for now in f_read: # 通过迭代器逐行访问
for nowByte in now: # 通过迭代器逐字符处理
newByte = nowByte ^ ord(password[count % len(password)])
count += 1
f_write.write(bytes([newByte]))


end = datetime.datetime.now()
print("文件加密完毕^_^", (end - start))

# 解密(因为我们采取的异或解密,所以其实和加密算法一样)
def decryFile(path, password):
start = datetime.datetime.now()
fileFullName = path.split(os.path.sep) # os.path.sep为操作系统的文件分隔符
fileName = fileFullName[len(fileFullName) - 1].split(".")[0]
fileSuffix = fileFullName[len(fileFullName) - 1].split(".")[1]

# print("文件全名称:", fileFullName[len(fileFullName)-1])
# print("文件名称:", fileName)
# print("文件后缀:", fileSuffix)

fileParent = path[0:len(path) - len(fileFullName[len(fileFullName) - 1])]
newFileName = "解密_" + fileFullName[len(fileFullName) - 1]
newFilePath = fileParent + newFileName

# print("文件父路径:", fileParent)
# print("新的文件名称:", newFileName)
# print("新的文件全路径:", newFilePath)

with open(path, "rb") as f_read:

with open(newFilePath, "wb") as f_write:

count = 0 # 当前密码解密索引

#为了防止解密后的文件出现乱码问题,而我们又无法预知编码,所以采用二进制读文件,字节流解密
# 我们采用异或循环解密
for now in f_read: # 通过迭代器逐行访问
for nowByte in now: # 通过迭代器逐字符处理
newByte = nowByte ^ ord(password[count % len(password)])
count += 1
f_write.write(bytes([newByte]))


end = datetime.datetime.now()
print("文件解密完毕", (end - start))


# # 测试文件的编码方式
# def encoding_way(path):
# f = open(path, 'rb')
# f_read = f.read()
# f_charInfo = chardet.detect(f_read)
# return f_charInfo['encoding']




if __name__ == '__main__':
choose=input('输入A字符串加密,输入B字符串解密,输入C文件加密,输入D文件内解密,其它关闭:')
if choose=='A':
plaintext=input('请输入加密文字明文:')
key=input('请输入加密密钥:')
print('密文',encode(plaintext,key))
if choose=='B':
ciphertext = input('请输入解密文字明文:')
key = input('请输入解密密钥:')
print('明文',decode(ciphertext,key))

if choose=='C':
print("进入文件加密模块")
path = str(input('请输入文件全路径:')).strip()
if os.path.isfile(path) :
password = str(input('请输入文件加密密码:')).strip()
encryFile(path, password)
else :
print("您输入的文件路径不存在")

if choose=='D':
print("进入文件解密模块")
path = str(input('请输入文件全路径:')).strip()
if os.path.isfile(path) :
password = str(input('请输入文件解密密码:')).strip()
decryFile(path, password)
else :
print("您输入的文件路径不存在")