当前位置:   article > 正文

一个简单的加密Web(vue 3 + Flask)(SM4、RSA、DES等五种加密算法)_vue sm4加密

vue sm4加密

前言

        学校开了一门密码学的课程,唯一比较感兴趣的是老师关于比特币的一些介绍,旷工、矿池、攻击,以及挖矿的原理,听起来真是不亦乐乎。快要结课了来了一个作业,让我们用Python是实现五种常用的加密方式,有对称加密如DES、SM4,也有非对称加密如RSA。要求就是简单实现,供用户使用的web前端是扩展,但是想着这个作业占20分的期末总分,还是卷一卷吧。于是想到之前用uni-app与flask搭建的智能监控,前端提供用户可以交互的界面,后端通过FLask设置的路由执行相应的操作,这个项目当然也可以按这个思路来。

技术栈

        前端:Vue 3

        组件:Element Plus

        后端:Flask

        语言:Python、Typescript、H5

        功能库:Axios(http请求)、CORS(跨域)、Gmmsl(SM4加密库)、pyDes(DES加密库)

效果展示

前端

主页

文件上传                                                        加密算法选择

操作按钮

后端

实现函数以及两个路由

原文:

DES加密:

移位加密:

一次一密:

RSA:

SM4:

代码

前端
主页
  1. <template>
  2. <div class="container">
  3. <div class="container header">
  4. <span>文件加密软件</span>
  5. </div>
  6. <div class="container main">
  7. <el-upload
  8. class="upload-demo"
  9. drag
  10. action="https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15"
  11. multiple
  12. :headers="headers"
  13. :file-list="fileList"
  14. :on-change="handleChange"
  15. :auto-upload="false"
  16. >
  17. <el-icon class="el-icon--upload"><upload-filled /></el-icon>
  18. <div class="el-upload__text">推动文件到这里或者<em>点击我</em></div>
  19. <template #tip>
  20. <div class="el-upload__tip">
  21. jpg/png files with a size less than 500kb
  22. </div>
  23. </template>
  24. </el-upload>
  25. <div class="slect">
  26. <span>选择加密的算法</span>
  27. <el-select v-model="optionValue" placeholder="Select">
  28. <el-option
  29. v-for="item in options"
  30. :key="item.value"
  31. :label="item.label"
  32. :value="item.value"
  33. :disabled="item.disabled"
  34. />
  35. </el-select>
  36. </div>
  37. <div class="button">
  38. <el-button type="danger" @click="dialogOfUpload = false"
  39. >取 消</el-button
  40. >
  41. <el-button type="primary" @click="confirmUpload()">确 认</el-button>
  42. <el-button @click="downloadFile()">下 载</el-button>
  43. </div>
  44. </div>
  45. </div>
  46. </template>
  47. <script setup lang="ts">
  48. import { VueElement, reactive, ref, resolveComponent } from "vue";
  49. import { UploadFilled } from "@element-plus/icons-vue";
  50. // import getItem from "@/api";
  51. import axios from "axios";
  52. import { ElMessage } from "element-plus";
  53. // 设置axios的基准地址
  54. axios.defaults.baseURL = "http://127.0.0.1:5000";
  55. const fileList = reactive([]);
  56. const headers = reactive({
  57. "Content-Type": "multipart/form-data",
  58. });
  59. // 接受上传文件的列表
  60. const handleChange = (file, newFileList) => {
  61. fileList.value = newFileList;
  62. };
  63. const dialogOfUpload = ref(false);
  64. const optionValue = ref("");
  65. const options = [
  66. {
  67. value: "ONCECryption",
  68. label: "ONCECryption",
  69. },
  70. {
  71. value: "MOVECryption",
  72. label: "MOVECryption",
  73. },
  74. {
  75. value: "DESCryption",
  76. label: "DESCryption",
  77. },
  78. {
  79. value: "SM4Cryption",
  80. label: "SM4Cryption",
  81. },
  82. {
  83. value: "RSACryption",
  84. label: "RSACryption",
  85. },
  86. ];
  87. // 处理文件下载操作
  88. const downloadFile = () => {
  89. return new Promise((resolve, reject) => {
  90. // 配置axios参数
  91. axios({
  92. url: "/api/download",
  93. method: "get",
  94. params: { operation: optionValue.value },
  95. responseType: "blob",
  96. })
  97. // 请求成功
  98. .then((res) => {
  99. try {
  100. // 使用 blob 接受后端数据
  101. const blob = new Blob([res.data], {
  102. type: res.headers["Content-Type"],
  103. });
  104. console.log("blob", blob);
  105. // 创建一个链接并设置内容
  106. const url = window.URL.createObjectURL(blob);
  107. // 创建一个链接元素以创建下载
  108. const a = document.createElement("a");
  109. a.style.display = "none";
  110. a.href = url;
  111. a.download = "sample.txt";
  112. document.body.append(a);
  113. // 触发下载
  114. a.click();
  115. // 清理
  116. window.URL.revokeObjectURL(url);
  117. } catch (e) {
  118. console.log("下载错误", e);
  119. }
  120. })
  121. .catch((err) => {
  122. console.error(err);
  123. });
  124. });
  125. };
  126. // 请求后端对上传的文件进行加密
  127. const confirmUpload = () => {
  128. // 文件 需要使用FormData对象来存储
  129. var param = new FormData();
  130. fileList.value.forEach((val, index) => {
  131. param.append("file", val.raw);
  132. param.append("optionValue", optionValue.value);
  133. });
  134. axios
  135. .post("/api/encryption", param)
  136. .then((res) => {
  137. console.log("response", res);
  138. ElMessage({
  139. message: "加密成功,请点击下载",
  140. duration: 5000,
  141. type: "success",
  142. });
  143. })
  144. .catch((err) => {
  145. ElMessage({
  146. message: "加密失败,请查看路径是否正确",
  147. duration: 1000,
  148. type: "error",
  149. });
  150. console.log("err", err);
  151. });
  152. };
  153. </script>
  154. <style lang="scss" scoped>
  155. @import "./index.scss";
  156. </style>
样式
  1. .container {
  2. width: 100%;
  3. height: 95vh;
  4. // background: url("./images/bgNew.jpg") no-repeat;
  5. background-color: rgb(241, 242, 244);
  6. display: flex;
  7. align-items: center;
  8. // justify-content: center;
  9. flex-direction: column;
  10. padding: 10px;
  11. .header {
  12. font-family: YouSheBiaoTiHei;
  13. font-size: 48px;
  14. line-height: 78px;
  15. width: 95%;
  16. height: 15%;
  17. border: 1px solid rgb(233, 231, 231);
  18. border-radius: 6px;
  19. background-color: #ffffff;
  20. margin-top: 10px;
  21. box-shadow: 0 0 12px rgba(0, 0, 0, 0.5);
  22. }
  23. .main {
  24. width: 95%;
  25. height: 80%;
  26. border: 1px solid rgb(233, 231, 231);
  27. border-radius: 6px;
  28. background-color: #ffffff;
  29. margin-top: 10px;
  30. box-shadow: 0 0 12px rgba(0, 0, 0, 0.5);
  31. align-items: center;
  32. justify-content: center;
  33. .slect {
  34. display: flex;
  35. flex-direction: column;
  36. margin-top: 10px;
  37. margin-bottom: 10px;
  38. // width: 100%;
  39. height: 100px;
  40. }
  41. }
  42. }
后端
  1. # 编写程序实现加密算法
  2. import binascii
  3. import io
  4. import os
  5. import random
  6. from gmssl.sm4 import CryptSM4, SM4_ENCRYPT, SM4_DECRYPT
  7. from pyDes import des, PAD_PKCS5, ECB
  8. from flask import Flask, request, send_file
  9. from flask_cors import CORS
  10. app = Flask(__name__)
  11. CORS(app, resources=r'/*')
  12. raw_binary_list = []
  13. content = ""
  14. # 查看是否会将‘\n’ 也计算进去
  15. # binary_dic = dict()
  16. # for binary in binary_content.split(" "):
  17. # raw_str = chr(int(binary, 2))
  18. # if raw_str not in binary_dic.keys():
  19. # binary_dic[raw_str] = 1
  20. # else:
  21. # binary_dic[raw_str] += 1
  22. # print(binary_dic.items())
  23. # 一次一密
  24. def generate_binary(length):
  25. return ''.join(random.choice('01') for _ in range(length))
  26. def once_oneCryption():
  27. # print("原始文本为:", " ".join(raw_binary_list))
  28. # 获取每个字符编码后的长度,并生成相同长度的随机文件作为密钥
  29. random_binary_list = [generate_binary(len(cha)) for cha in raw_binary_list]
  30. # print("密钥为:", " ".join(random_binary_list))
  31. with open("ONCE_key.txt", "w", encoding="utf-8") as file_write:
  32. file_write.write(" ".join(random_binary_list))
  33. print('ONCE 密钥存储成功')
  34. # 与原始文件按位异或,实现加密
  35. ciphertext = " ".join([str(bin(int(raw_binary_list[index], 2) ^ int(random_binary_list[index], 2))).replace('0b', '') for index in range(len(random_binary_list))])
  36. # 打印密文
  37. with open("ONCE_encryption.txt", "w", encoding="utf-8") as file_write:
  38. file_write.write(ciphertext)
  39. print("ONCE 加密成功")
  40. # 将密文与密钥按位异或,实现解密
  41. cryption_list = ciphertext.split(" ")
  42. # 得到原文二进制文件
  43. progress_list = [int(cryption_list[index], 2) ^ int(random_binary_list[index], 2)for index in range(len(cryption_list))]
  44. # print("progress_list", progress_list)
  45. # 恢复二进制为字符
  46. raw_text = "".join([str(chr(binary)) for binary in progress_list])
  47. # 打印文件内容
  48. with open("ONCE_decryption.txt", "w", encoding="utf-8") as file_write:
  49. file_write.write(raw_text)
  50. print("ONCE 解密成功")
  51. # 移位加密
  52. def move_cryption():
  53. # 生成随机密钥1~255
  54. key = random.randint(1, 255)
  55. # key = 200
  56. with open('MOVE_key.txt', mode='w', encoding='utf-8') as writer:
  57. writer.write(str(key))
  58. print('MOVE 密钥存储成功')
  59. # 原始文本
  60. raw_value_list = [int(binary, 2) for binary in raw_binary_list]
  61. # print("原始文本", raw_value_list)
  62. # 加密
  63. cryption_list = []
  64. for binary in raw_binary_list:
  65. if int(binary, 2) + key > 255:
  66. cryption_list.append(str(int(binary, 2) + key - 255))
  67. else:
  68. cryption_list.append(str(int(binary, 2) + key))
  69. with open('MOVE_encryption.txt', mode='w', encoding='utf-8') as writer:
  70. writer.write(''.join(cryption_list))
  71. print('MOVE 加密成功')
  72. # print("密文为:", cryption_list)
  73. # 解密
  74. raw_text_list = "".join([str(chr(int(value) - key + 255)) if (int(value) - key) < 255 else str(chr(int(value) - key)) for value in cryption_list])
  75. with open('MOVE_decryption.txt', mode='w', encoding='utf-8') as writer:
  76. writer.write(raw_text_list)
  77. print('MOVE 解密成功')
  78. def DES():
  79. # 随机字符串作为密钥 长度未为8
  80. key_length = 8
  81. key = "".join([random.choice('01') for index in range(key_length)])
  82. with open('DES_key.txt', mode='w', encoding='utf-8') as writer:
  83. writer.write(key)
  84. print('DES 密钥存储成功')
  85. # 使用pyDes 产生密钥 对原文进行加密
  86. # 创建加密器
  87. k = des(key, ECB, padmode=PAD_PKCS5)
  88. # print("原文:", content)
  89. # 将文本内容编码为 utf-8
  90. encoded_content = content.encode('utf-8')
  91. encryption = k.encrypt(encoded_content)
  92. with open('DES_encryption.txt', mode='w', encoding='utf-8') as writer:
  93. writer.write(str(encryption))
  94. print('DES 加密成功')
  95. # 使用pyDes 密钥 对原文进行解密
  96. raw_text_encoded = k.decrypt(encryption)
  97. raw_text = raw_text_encoded.decode('utf-8')
  98. with open('DES_decryption.txt', mode='w', encoding='utf-8') as writer:
  99. writer.write(raw_text)
  100. print('DES 解密成功')
  101. def SM4_encrypt(crypt_sm4, key, value):
  102. """
  103. 加密
  104. :param crypt: 加密器
  105. :param key: 密钥
  106. :param value: 原文
  107. :return:密文
  108. """
  109. crypt_sm4.set_key(key, SM4_ENCRYPT)
  110. encrypt_value = crypt_sm4.crypt_ecb(value)
  111. return encrypt_value.hex()
  112. def str_to_hexStr(hex_str):
  113. """
  114. 字符串转hex
  115. :param hex_str:
  116. :return:
  117. """
  118. hex_data = hex_str.encode('utf-8')
  119. str_bin = binascii.unhexlify(hex_data)
  120. return str_bin.decode('utf-8')
  121. def SM4_decrypt(crypt_sm4, key, value):
  122. """
  123. 解密
  124. :param crypt_sm4: 解密器
  125. :param key: 密钥
  126. :param value: 密文
  127. :return: 原文
  128. """
  129. crypt_sm4.set_key(key, SM4_DECRYPT)
  130. # 将十六进制系列转换成字节序列
  131. decrypt_value = crypt_sm4.crypt_ecb(bytes.fromhex(value))
  132. return str_to_hexStr(decrypt_value.hex())
  133. def SM4():
  134. # 每次读取4个字节 32位 文本
  135. # 将文本进行encode
  136. encoded_content = content.encode('utf-8')
  137. # print(encoded_content)
  138. # 每次读入4个字
  139. chunk_size = 128
  140. start = 0
  141. end = start + chunk_size
  142. # 创建加密器
  143. crypt_sm4 = CryptSM4()
  144. # 随机生成一个密钥
  145. key = os.urandom(16)
  146. with open('SM4_key.txt', mode='w', encoding='utf-8') as writer:
  147. writer.write(f'{key}')
  148. print('SM4 密钥存储成功')
  149. encrypt_list = []
  150. decrypt_list = []
  151. # 加密
  152. while True:
  153. input = encoded_content[start: end - 1]
  154. start = end + chunk_size
  155. end = start + chunk_size
  156. if end > len(encoded_content):
  157. break
  158. # 加密
  159. encrypt_value = SM4_encrypt(crypt_sm4, key, input)
  160. encrypt_list.append(encrypt_value)
  161. with open('SM4_encryption.txt', mode='w', encoding='utf-8') as writer:
  162. writer.write(''.join(encrypt_list))
  163. print('SM4 加密成功')
  164. # 解密
  165. for item in encrypt_list:
  166. decrypt_value = SM4_decrypt(crypt_sm4, key, item)
  167. decrypt_list.append(decrypt_value)
  168. with open('SM4_decryption.txt', mode='w', encoding='utf-8') as writer:
  169. writer.write(''.join(decrypt_list))
  170. print('SM4 解密成功')
  171. def gcd(a, b):
  172. if b == 0:
  173. return a
  174. else:
  175. return gcd(b, a % b)
  176. def ext_gcd(a, b):
  177. if b == 0:
  178. x1 = 1
  179. y1 = 0
  180. x = x1
  181. y = y1
  182. r = a
  183. return r, x, y
  184. else:
  185. r, x1, y1 = ext_gcd(b, a % b)
  186. x = y1
  187. y = x1 - a // b * y1
  188. return r, x, y
  189. def get_key():
  190. # 输入两个素数
  191. p = 11
  192. q = 13
  193. # 计算n = P X Q \ Z = ( P -1 ) X ( Q - 1)
  194. n = p * q
  195. z = (p - 1) * (q - 1)
  196. # 计算公私密钥 e X d = 1 (mod n) 通过欧几里得与扩展的欧几里得算法得到
  197. e = z # 让e与z互素 且 d > 0
  198. while e > 1:
  199. e -= 1
  200. r, x, y = ext_gcd(e, z)
  201. if (gcd(e, z) == 1) & (x > 0):
  202. break
  203. d = x
  204. return (n, e), (n, d)
  205. def RSA_encrypt(x, key):
  206. n = key[0]
  207. e = key[1]
  208. y = x ** e % n
  209. return y
  210. def RSA_decrypt(y, key):
  211. n = key[0]
  212. d = key[1]
  213. x = y ** d % n
  214. return x
  215. def RSA():
  216. # 生成公私钥
  217. pub_k, pri_k = get_key()
  218. with open('RSA_key.txt', mode='w', encoding='utf-8') as writer:
  219. writer.write(f'{pub_k}, {pri_k}')
  220. print('RSA 密钥存储成功')
  221. # 每次读取4个字节 32位 文本
  222. # 将文本进行encode
  223. encoded_content = content.encode('utf-8')
  224. # print(encoded_content)
  225. # 每次读入4个字
  226. start = 0
  227. encrypt_list = []
  228. decrypt_list = []
  229. # 加密
  230. while True:
  231. input = content[start]
  232. start += 1
  233. if start >= len(content):
  234. break
  235. # print(input)
  236. # 加密
  237. y = RSA_encrypt(ord(input), pub_k)
  238. encrypt_list.append(f'{y}')
  239. # print('加密后', encrypt_list)
  240. with open('RSA_encryption.txt', mode='w', encoding='utf-8') as writer:
  241. writer.write(''.join(encrypt_list))
  242. print("RSA 加密成功")
  243. # 解密
  244. for item in encrypt_list:
  245. # 解密
  246. x = RSA_decrypt(int(item), pri_k)
  247. decrypt_list.append(chr(x))
  248. with open('RSA_decryption.txt', mode='w', encoding='utf') as writer:
  249. writer.write(''.join(decrypt_list))
  250. print('RSA 解密成功')
  251. def read_file(file_name):
  252. # 文件路径
  253. # file_name = "practice_record.txt"
  254. global content
  255. # 读取文件
  256. with open(file_name, mode="r", encoding="utf-8") as file_reader:
  257. content = file_reader.read()
  258. # 将文件字符串进行编码 返回字符串的unicode编码 A -> 65
  259. global raw_binary_list
  260. raw_binary_list = [bin(ord(item)).replace('0b', '') for item in content]
  261. @app.route('/api/download', methods=['GET'])
  262. def down_file():
  263. operation = request.args.get('operation')
  264. print(operation)
  265. content = ''
  266. # 一次一密
  267. if operation == "ONCECryption":
  268. # 以二进制模式打开文本
  269. with open('ONCE_encryption.txt', 'rb') as reader:
  270. content = reader.read()
  271. # 将文本内容转化为字节流
  272. stream = io.BytesIO(content)
  273. return send_file(stream, as_attachment=True, attachment_filename="ONCE_encryption.txt", mimetype='application/octet-stream')
  274. elif operation == "MOVECryption":
  275. # 以二进制模式打开文本
  276. with open('MOVE_encryption.txt', 'rb') as reader:
  277. content = reader.read()
  278. # 将文本内容转化为字节流
  279. stream = io.BytesIO(content)
  280. return send_file(stream, as_attachment=True, attachment_filename="MOVE_encryption.txt",
  281. mimetype='application/octet-stream')
  282. elif operation == "DESCryption":
  283. # 以二进制模式打开文本
  284. with open('DES_encryption.txt', 'rb') as reader:
  285. content = reader.read()
  286. # 将文本内容转化为字节流
  287. stream = io.BytesIO(content)
  288. return send_file(stream, as_attachment=True, attachment_filename="DES_encryption.txt",
  289. mimetype='application/octet-stream')
  290. elif operation == "SM4Cryption":
  291. # 以二进制模式打开文本
  292. with open('SM4_encryption.txt', 'rb') as reader:
  293. content = reader.read()
  294. # 将文本内容转化为字节流
  295. stream = io.BytesIO(content)
  296. return send_file(stream, as_attachment=True, attachment_filename="SM4_encryption.txt",
  297. mimetype='application/octet-stream')
  298. elif operation == "RSACryption":
  299. # 以二进制模式打开文本
  300. with open('RSA_encryption.txt', 'rb') as reader:
  301. content = reader.read()
  302. # 将文本内容转化为字节流
  303. stream = io.BytesIO(content)
  304. return send_file(stream, as_attachment=True, attachment_filename="RSA_encryption.txt",
  305. mimetype='application/octet-stream')
  306. else:
  307. return "error"
  308. @app.route('/api/encryption', methods=['POST', 'GET'])
  309. def handle_cryption():
  310. # 接受上传的文件
  311. fileList = request.files.getlist("file")
  312. # 接受需要执行的加密算法
  313. cryption_type = request.form.get("optionValue")
  314. file_name = ""
  315. for file in fileList:
  316. # 读取数据
  317. file_stream = file.stream.read()
  318. with open(file.filename, 'wb') as writer:
  319. writer.write(file_stream)
  320. file_name = file.filename
  321. print(fileList[0], cryption_type)
  322. # 读取文件 得到原始文本和二进制的列表
  323. read_file(file_name)
  324. if cryption_type == "ONCECryption":
  325. once_oneCryption()
  326. elif cryption_type == "MOVECryption":
  327. move_cryption()
  328. elif cryption_type == "DESCryption":
  329. DES()
  330. elif cryption_type == "SM4Cryption":
  331. SM4()
  332. elif cryption_type == "RSACryption":
  333. RSA()
  334. else:
  335. return "error"
  336. return "data received"
  337. if __name__ == '__main__':
  338. # once_oneCryption()
  339. # move_cryption()
  340. # DES()
  341. # SM4()
  342. # RSA()
  343. # print(raw_binary_list)
  344. app.run(debug=True)

技术实现

        界面设计

        该项目对界面美观要求不高,功能也相对简单。采用一个上下占比2:8的布局,整体背景使用浅灰色,上下两个部分单独使用div包裹,对边框进行圆润以及添加的阴影使其与背景分离,简单地形成一定的层次感。

        文件上传使用element plus中提供的组件,支持拖拽等功能,将其放置在最上面,紧接着下方放置一个多选框,用户可以选择自己需要使用的加密算法,最后提供三个按钮,取消、确定、下载。

        功能分析

        文件上传:elemeng plus在原生upload上进行开发,提供了可拖拽功能。为了能够获取到上传的文件,先初始化一个变量fileList,在el-upload中使用:file-list动态获取上传的文件,当上传多个文件时,文件会以列表的形式存储,而后使用axios讲文件封装成formdata格式的数据进行传输。

        算法选择:使用el-elect组件,配置选项参数。初始化一个optionValue变量,将该变量放置在el-elect组件中使用:optionValue动态接受选项的值。

        传输文件与选择的算法:文件使用变量filelist存储、算法使用变量optionValue存储。在后端已经启动了一个flask服务,地址为http://localhost,并且配置了加密路由与下载路由。配置axios目标地址、请求方法、传输数据,向后端发送。后端接受请求的参数,执行不同的加密算法。

        下载文件:前端使用axios向后端下载路由发送需要下载的加密后的文件,后端以字节流的形式发送到前端,前端使用blob存放数据,创建一个链接并放置blob数据,再创建一个<a>标签用于触发下载事件。

        接受文件并加密:后端使用flask启动一个web服务,将接受文件与执行加密操作放置在函数中,并为该函数配置一个路由地址,使得前端能够通过后端ip以及路由地址请求该函数。后端使用request函数接收前端传来的参数,根据参数调用不同的函数执行不同的加密算法。密钥与密文都会存储在本地的项目的根目录下。

        返回密文文件:接收到前端发来的下载密文文件的请求,根据参数读取相应算法加密后的密文,读取文件时要配置open函数中mode参数为‘rb’,因为需要将文件以二进制读取,而后再通过send_file将二进制以字节流的方式发往前端。

问题与解决

        1. 前后发送request请求,报了bad request的错误,大概率是因为前后发送与接收数据的方式不一致,比如前端使用下面的方式发送params,后端需要使用request.args.get()的方式接收

  1. axios({
  2. url: "/api/download",
  3. method: "get",
  4. params: { operation: optionValue.value },
  5. responseType: "blob",
  6. })

        2. 针对跨域问题,在后端使用flask框架时,可以直接在后端使用CORS库解决这个问题,不需要在前端进行配置。

        3. 针对前端下载文件这种任务,后端不能直接传文件,需要将文件以二进制的形式读取,再通过字节流的形式发送。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小桥流水78/article/detail/969409
推荐阅读
相关标签
  

闽ICP备14008679号