【项目】HDU网安实验——可视化的RSA加密系统

【项目】HDU网安实验——可视化的RSA加密系统

前言

看到老师布置下这个作业的时候,我已经是第二次写类似的实验了,所以打算挑战一下自己,让整个系统的代码都由我自己的一个人完成。通过参考各种资料少“借鉴源码”从头到尾从内到外理解这个系统。所以认真写代码的我在完成实验之后想把自己的成就,感悟和体会分享出去。也可以造福造福后来人,当一篇优质博文。

这篇文章尽量从我开发过程的顺序来编写,着力于我遇到的问题,把它剖析出来,用一个新手的视角说出我写实验的过程。

但这篇文章的视角并不会细微到每一行代码的含义,而是从不同的功能角度,把一个个功能拼接组装成一个大系统。毕竟根据我的经历,我们一定很需要这样类型的文章。

希望这样的文章可以帮助大家解决疑惑!

实验任务

选取DES,RSA,SM2,SM3,SM4或者ECC算法中的一个,使用自己熟悉的语言,编程实现对明文“我喜欢上课,我愿意接受这1次challenge!”

扩展功能: 实现各类文件的加解密(文件类型不限,文件大小不限)

编程开发环境

  • 编程语言:Python
  • 可视化开发:WxPython库
pip install wxPython
# 框架内自动生成
import wx
import wx.xrc
  • 密钥生成: Crypto.PublicKey库中的RSA库
pip install pycryptodome 
form Crypto.PublicKey import RSA
  • 编程软件:Pycharm
  • 可视化开发软件:WxFormBuilder
    可以参考另一篇博文,初步介绍了WxFormBuilder的安装和使用。至于怎样完成你需要的开发,还得各位同学多多摸索,这并不困难,只是需要你的一点时间。
    Python开发图形可视化界面程序

核心原理与关键代码实现分析

RSA加密系统设计流程简介

我完成此次实验的设计思路主要如下图,将一个任务分成四个部分,每个部分完成的时候都进行一次对这个部分的测试,这样更便于我发现问题和解决问题,提高效率,完善我的设计思路。

设计要求

我期望设计一个有良好操作逻辑的一个加密程序,希望做到每一步用户都能得到相应的反馈,可以便捷地选择加密的明文和解密的密文还有密钥,并且能实现任意大小文件的加解密
加密系统设计思路

RSA核心原理介绍

RSA是一种 非对称加密算法,安全性的根源来自于将两个大质数相乘很容易,但是想要将其乘积分解成原始的质数因子却非常困难。 RSA的密钥分为公钥pk(n,e) n为大质数,作为模数,e是公钥指数;私钥pk(n,d) n是大质数,d是私钥指数。加解密的过程如图:
在这里插入图片描述
在这里插入图片描述

密钥的生成与保存

密钥的生成主要依赖Python的第三方库,引入库的方法已在 编程环境部分 提出。生成密钥的过程就是调用RSA库,确定密钥空间的大小,再将密钥对保存入用于存储密钥的文件格式。保存成文件的主要目的是为了后续加密解密的使用,就像是真正的钥匙一样。

	# 功能实现部分
	def generate_key_pair(self, key_size):
		key_size = 2048
		key = RSA.generate(key_size)
		private_key = key.export_key()
		public_key = key.publickey().export_key()
		with open('pr.pem', 'wb') as file:
			file.write(private_key)
		with open('pk.pem', 'wb') as file:
			file.write(public_key)
		
		# 可视化部分
		# 更新文本框内容,用于提示
		self.feedback.SetLabel("密钥已生成")
		# 其实根据代码的意思,这个返回获取并不需要,
		#因为解析密钥的时候都是利用pem文件,并不是这里的密钥对象,
		#但是鉴于整个系统可以正常运行,还是不随便进行改动了
		return private_key, public_key

密钥文件有它的专属格式,也就是这里的pk.pem和pr.pem可以先看看pem文件的内容,
在这里插入图片描述
可以看到pem文件的内容并不是可以直接用于数学计算的类型,我们稍后还需要将这个格式的文件转换成可以进行数学计算的大整数

RSA核心原理的代码实现

如果只是把明文x对应的整数和e,d对应的指数进行乘方计算再取模,这对于计算机是一个难以完成的任务,所以我们需要通过修改我们的算法加速指数取模运算,所以引入平方乘算法,用于RSA加密算法核心的实现。

# RSA加密的核心,大指数的取模运算,用平方乘算法来加速
	def square_and_multiply(self, base, exponent, modulus):
		result = 1
		base = base % modulus

		while exponent > 0:
			# 如果指数为奇数,乘以当前的base
			if exponent % 2 == 1:
				result = (result * base) % modulus

			# 将指数减半,base平方
			exponent = exponent // 2
			base = (base ** 2) % modulus

		return result

辅助函数

我们都知道,RSA的加密运算是基于整数的,要想进行加密算法,我们就需要获取基数,指数和模数三个整数。本质上,辅助函数就是一系列的格式转换功能函数。
公钥私钥信息提取函数就是把密钥pem文件中的模数和指数提取成大整数的的过程。

	# 提取私钥参数
	def extract_rsa_parameters_from_pr(self, event):
		# 可视化部分,从私钥文件选择器获取文件路径
		file_path = self.private_key_input.GetPath()
		# 从 PEM 文件中提取私钥对象
		private_key = RSA.import_key(open(file_path).read())

		# 提取关键参数
		modulus_n = private_key.n
		private_exponent_d = private_key.d

		return modulus_n, private_exponent_d


	# 公钥信息提取
	def extract_rsa_parameters_from_pk(self, event):
		# 可视化部分,从命名为public_key_input的控件(也就是公钥的文件选择器)获取文件的路径
		file_path = self.public_key_input.GetPath()
		# 提取公钥的模数和指数
		public_key = RSA.import_key(open(file_path).read())
		modulus_n = public_key.n
		public_exponent_e = public_key.e

		return modulus_n, public_exponent_e

这个函数本身并不难,重点是在于要找到这个库对应的方法,从pem文件中提取到整数。给大家看看提取完参数之后的公钥私钥文件的样子
在这里插入图片描述
此外,我们还需要把字节流类型的文件转换成大整数进行加密运算。

# 辅助函数,将大整数转换为字节流
	def int_to_bytes(self, integer):
		byte_stream = integer.to_bytes((integer.bit_length() + 7) // 8, byteorder='big')
		return byte_stream

加解密流程实现

我的系统设计考虑到要实现对任意文件大小的加密,这里就要涉及到分组加密的方式。
我使用了一个while循环和CHUNK_SIZE变量来进行分组加密;整个的加密流程是:

  1. 先将明文按照CHUNK_SIZE分成一个个等大的分组
  2. 然后将每个分组内的文件 字节流 转换成大整数
  3. 根据获取到的公钥指数和模数,进行平方乘运算,得出的结果是一个加密后的 全新的整数
  4. 将这个整数转换成字节流 (Python内置函数) 写入文件,同时还要将每个分组经过加密之后的密文长度 (未加密) 保存到文件之中。

第四步的主要作用是确保解密的时候解密的是一个完整的意义分组,在面对一个中文字占多个字节的情况下,可以避免解密之后出现乱码。稍后会解释

# 加密运算
def encrypt_file_internal(self, input_file, output_file, public_key_params):
	# 获取模数和指数
	modulus_n, public_exponent_e = public_key_params
	# 规定分组的大小,我选择的是255,理论上只要分组不大于密钥的大小就好,我用的是2048位的密钥(256B)
	CHUNK_SIZE = 255
	with open(input_file, 'rb') as in_file, open(output_file, 'wb') as out_file:
		while True:
			chunk = in_file.read(CHUNK_SIZE)
			if not chunk:
				break
			# 将每个分组的字节流转换成大整数
			d_data = int.from_bytes(chunk, byteorder='big')
			# 得到加密之后的大整数
			ciphertext_number = self.square_and_multiply(d_data, public_exponent_e, modulus_n)
			# 把密文大整数换成字节流写入文件
			ciphertext = self.int_to_bytes(ciphertext_number)
			# 把每个分组经过加密之后的密文长度保存到文件中,使得解密的时候可以按照正确的长度解密,保证无乱码
			out_file.write(len(ciphertext).to_bytes(4, byteorder='big'))
			out_file.write(ciphertext)
			# 更新文本框内容,在可视化界面提示加密已经完成
			self.feedback.SetLabel("加密完成")

解密流程和加密流程类似,在读取到私钥指数和模数之后,在密文中还需要读取每个分组的长度,确保意义完整。值得一提的是,为了保证解密之后的意义完整,还需要经过一步操作,就是将每个分组经过解密之后得出的大整数进行拼接,合成一个全新的大整数,再将大整数统一转换成字节流一次性写入文件。因为意义的完整性的变化是出现在由大整数转换成字节流的部分的。

举个例子:如果第一个分组解密之后的整数是123,第二个分组是456;如果每解密一个分组就写进一次文件,那么123对应的字节流b’0xaa\0xbb\0xcc(举例说明并非真实字节流)和456对应的字节流b’0xff\0xee\0xdd就会被分别被转码写入文件,如果此时这个字节流里含有中文字,并且正好是第一个分组内的0xcc\0xff,那么转码写入文件之后这个字就被拆开,导致全文乱码。

因此这个操作是对于解密的意义完整性是至关重要的。

def decrypt_file_internal(self, input_file, output_file, private_key_params):
	modulus_n, private_exponent_d = private_key_params
	# 用于存放每个分组解密之后的大整数
	plaintext_numbers = []

	with open(input_file, 'rb') as in_file:
		while True:
			# 先读取每个分组的长度
			length_bytes = in_file.read(4)
			if not length_bytes:
				break
			length = int.from_bytes(length_bytes, byteorder='big')
			# 按照分组的长度来解密
			ciphertext = in_file.read(length)
			plaintext_number = self.square_and_multiply(int.from_bytes(ciphertext, byteorder='big'),
													private_exponent_d, modulus_n)
			# 把每个分组的大整数拼接到一起,进行整体的大整数转换成字节流,不然会出现“一个中文字占多个字节,在分组的时候一个中文字被
			# 切割,要是一个大整数一个大整数的转换会有乱码“的问题,所以一定需要把所有分组的大整数拼接到一起进行转换
			plaintext_numbers.append(plaintext_number)

	concatenated_plaintext = int.from_bytes(b"".join(self.int_to_bytes(p) for p in plaintext_numbers),
											byteorder='big')

	with open(output_file, 'wb') as out_file:
		out_file.write(self.int_to_bytes(concatenated_plaintext))

	# 可视化部分
	# 读取解密后的文件内容
	with open(output_file, 'rb') as decrypted_file:
		decrypted_content = decrypted_file.read().decode('utf-8')

	# 设置文本框内容为解密后的文件内容
	self.feedback.SetLabel(f"解密完成: {decrypted_content}")

可视化开发

在完成加解密骨干内容之后,下一步就是为这些核心添加一个可视化框架。我采用的可视化开开发库是Wxpython,并且选择了WxForrmBuilder这个可视化开发工具。
整体的开发流程是:

  1. 搭建好程序可视化界面布局
  2. 选择合适的控件
  3. 设置相对应的属性
  4. 绑定控件的事件
  5. 重命名各个控件和事件相应函数的名字
  6. 生成框架代码(可选择编程语言)
  7. 重写事件相应函数
  8. 补充辅助函数
  9. 补充主函数
  10. 测试运行

在这里插入图片描述
这是我开发完成的界面,完成开发后,就可以生成框架代码了!(生成代码方式和开发方式见前面的参考博文)

为了让用户使用体验更好,我将每个操作都补充了一些可视性提示。例如,解密后显示解密的文件内容。

# 读取解密后的文件内容
	with open(output_file, 'rb') as decrypted_file:
		decrypted_content = decrypted_file.read().decode('utf-8')

	# 设置文本框内容为解密后的文件内容
	self.feedback.SetLabel(f"解密完成: {decrypted_content}")

在这里插入图片描述

同时为了完成可视化界面的输入可以正确的传入内部函数,我们还需要设置一个过渡函数,用于获取可视化界面的输入

# 从不同的输入控件获取相关的参数,每个控件的名词都在wxformbuilder上重命名过了,便于区分
	def encrypt_file(self, event):
		# 从输入明文的文件选择器获取输入文件路径
		input_file_path = self.input_file_e.GetPath()
		# 从加密部分的密文文件选择器获取输出文件路径
		output_file_path = self.output_file_e.GetPath()
		# 获取公钥参数,是一个含有模数和指数的元组
		public_key_params = self.extract_rsa_parameters_from_pk(event)
		# 获取到相关参数之后,再写一个真正进行加密运算的函数,完成加密功能
		self.encrypt_file_internal(input_file_path, output_file_path, public_key_params)

	def decrypt_file(self, event):
		# 获取输入文件路径
		input_file_path = self.input_file_d.GetPath()
		# 获取输出文件路径
		output_file_path = self.output_file_d.GetPath()
		# 获取私钥参数
		private_key_params = self.extract_rsa_parameters_from_pr(event)
		# 进行解密运算
		self.decrypt_file_internal(input_file_path, output_file_path, private_key_params)

再补写主函数,经过测试就算开发完成了。

# 主函数
if __name__ == "__main__":
	# 创建一个应用程序对象
	app = wx.App()

	# 创建Frame类的实例
	frame = Frame(None)

	# 显示窗口
	frame.Show()

	# 进入主循环
	app.MainLoop()

RSA加密系统的使用

整个RSA加密系统的逻辑很简单,用户执行的步骤都有相应的反馈;加解密的过程就像是解锁锁和上锁,有了对应的公钥私钥对,把对应的文件放入相对应的位置就可以获取对应的明文和密文。
在这里插入图片描述
生成密钥之后,就会在源代码当前文件夹下生成两个pem文件。

这里我的输入都文件选择器类型,可以方便的进行文件选择和创建。首先选择明文。
在这里插入图片描述
再选择加密后密文文件路径。
在这里插入图片描述
点击加密。
在这里插入图片描述
点击加密之后,在你指定的路径下就会多一个密文文件,并提示你加密完成。最后你可以选择用私钥进行解密,下面是三份文件的对照。可以看到流程走通了,加解密功能圆满成功。

在这里插入图片描述

全部源代码附后

# 这是一个做了可视化开发的RSA文件加解密系统,编程的思路是面向对象的开发,先用wxformbuider 生成可视化的框架,也就是下面的Frame类,
# 再补充类的各种方法。写上主函数就完成了

# -*- coding: utf-8 -*-

###########################################################################
## Python code generated with wxFormBuilder (version 4.0.0-0-g0efcecf)
## http://www.wxformbuilder.org/
##
## PLEASE DO *NOT* EDIT THIS FILE!
###########################################################################

import wx
import wx.xrc
from Crypto.PublicKey import RSA

###########################################################################
## Class Frame
###########################################################################

class Frame ( wx.Frame ):
	# 每个控件模块的初始化
	def __init__( self, parent ):
		wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = u"RSA文件加解密", pos = wx.Point( -1,-1 ), size = wx.Size( 680,520 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL )

		self.SetSizeHints( wx.Size( -1,-1 ), wx.DefaultSize )
		self.SetBackgroundColour( wx.SystemSettings.GetColour( wx.SYS_COLOUR_INACTIVECAPTION ) )

		fgSizer7 = wx.FlexGridSizer( 1, 2, 0, 0 )
		fgSizer7.SetFlexibleDirection( wx.BOTH )
		fgSizer7.SetNonFlexibleGrowMode( wx.FLEX_GROWMODE_SPECIFIED )

		fgSizer8 = wx.FlexGridSizer( 1, 1, 0, 0 )
		fgSizer8.SetFlexibleDirection( wx.BOTH )
		fgSizer8.SetNonFlexibleGrowMode( wx.FLEX_GROWMODE_SPECIFIED )

		self.feedback = wx.TextCtrl(self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.Size(360, 650),
									wx.TE_MULTILINE | wx.TE_READONLY)
		fgSizer8.Add(self.feedback, 0, wx.ALL, 5)


		fgSizer7.Add( fgSizer8, 1, wx.EXPAND, 5 )

		bSizer6 = wx.BoxSizer( wx.VERTICAL )

		bSizer8 = wx.BoxSizer( wx.VERTICAL )

		self.m_staticText9 = wx.StaticText( self, wx.ID_ANY, u"明文路径:", wx.DefaultPosition, wx.DefaultSize, 0 )
		self.m_staticText9.Wrap( -1 )

		bSizer8.Add( self.m_staticText9, 0, wx.ALL, 5 )

		self.input_file_e = wx.FilePickerCtrl( self, wx.ID_ANY, wx.EmptyString, u"Select a file", u"*.*", wx.DefaultPosition, wx.DefaultSize, wx.FLP_DEFAULT_STYLE )
		bSizer8.Add( self.input_file_e, 0, wx.ALL, 5 )

		self.m_staticText10 = wx.StaticText( self, wx.ID_ANY, u"公钥:", wx.DefaultPosition, wx.DefaultSize, 0 )
		self.m_staticText10.Wrap( -1 )

		bSizer8.Add( self.m_staticText10, 0, wx.ALL, 5 )

		self.public_key_input = wx.FilePickerCtrl( self, wx.ID_ANY, wx.EmptyString, u"Select a file", u"*.*", wx.DefaultPosition, wx.DefaultSize, wx.FLP_DEFAULT_STYLE )
		bSizer8.Add( self.public_key_input, 0, wx.ALL, 5 )

		self.m_staticText11 = wx.StaticText( self, wx.ID_ANY, u"密文路径:", wx.DefaultPosition, wx.DefaultSize, 0 )
		self.m_staticText11.Wrap( -1 )

		bSizer8.Add( self.m_staticText11, 0, wx.ALL, 5 )

		self.output_file_e = wx.FilePickerCtrl( self, wx.ID_ANY, wx.EmptyString, u"Select a file", u"*.*", wx.DefaultPosition, wx.DefaultSize, wx.FLP_DEFAULT_STYLE )
		bSizer8.Add( self.output_file_e, 0, wx.ALL, 5 )


		bSizer6.Add( bSizer8, 1, wx.EXPAND, 5 )

		bSizer10 = wx.BoxSizer( wx.VERTICAL )

		self.encrypt = wx.Button( self, wx.ID_ANY, u"加密", wx.DefaultPosition, wx.DefaultSize, 0 )

		self.encrypt.SetDefault()

		self.encrypt.SetBitmapPosition( wx.TOP )
		bSizer10.Add( self.encrypt, 0, wx.ALL, 5 )


		bSizer6.Add( bSizer10, 1, wx.EXPAND, 5 )

		bSizer11 = wx.BoxSizer( wx.VERTICAL )

		self.m_staticText12 = wx.StaticText( self, wx.ID_ANY, u"密文路径:", wx.DefaultPosition, wx.DefaultSize, 0 )
		self.m_staticText12.Wrap( -1 )

		bSizer11.Add( self.m_staticText12, 0, wx.ALL, 5 )

		self.input_file_d = wx.FilePickerCtrl( self, wx.ID_ANY, wx.EmptyString, u"Select a file", u"*.*", wx.DefaultPosition, wx.DefaultSize, wx.FLP_DEFAULT_STYLE )
		bSizer11.Add( self.input_file_d, 0, wx.ALL, 5 )

		self.m_staticText13 = wx.StaticText( self, wx.ID_ANY, u"私钥", wx.DefaultPosition, wx.DefaultSize, 0 )
		self.m_staticText13.Wrap( -1 )

		bSizer11.Add( self.m_staticText13, 0, wx.ALL, 5 )

		self.private_key_input = wx.FilePickerCtrl( self, wx.ID_ANY, wx.EmptyString, u"Select a file", u"*.*", wx.DefaultPosition, wx.DefaultSize, wx.FLP_DEFAULT_STYLE )
		bSizer11.Add( self.private_key_input, 0, wx.ALL, 5 )

		self.m_staticText14 = wx.StaticText( self, wx.ID_ANY, u"解文路径:", wx.DefaultPosition, wx.DefaultSize, 0 )
		self.m_staticText14.Wrap( -1 )

		bSizer11.Add( self.m_staticText14, 0, wx.ALL, 5 )

		self.output_file_d = wx.FilePickerCtrl( self, wx.ID_ANY, wx.EmptyString, u"Select a file", u"*.*", wx.DefaultPosition, wx.DefaultSize, wx.FLP_DEFAULT_STYLE )
		bSizer11.Add( self.output_file_d, 0, wx.ALL, 5 )


		bSizer6.Add( bSizer11, 1, wx.EXPAND, 5 )

		bSizer12 = wx.BoxSizer( wx.VERTICAL )

		self.decrypt = wx.Button( self, wx.ID_ANY, u"解密", wx.DefaultPosition, wx.DefaultSize, 0 )

		self.decrypt.SetDefault()
		bSizer12.Add( self.decrypt, 0, wx.ALL, 5 )

		bSizer61 = wx.BoxSizer( wx.VERTICAL )

		self.generate_key = wx.Button( self, wx.ID_ANY, u"生成密钥", wx.DefaultPosition, wx.DefaultSize, 0 )
		bSizer61.Add( self.generate_key, 0, wx.ALL, 5 )


		bSizer12.Add( bSizer61, 1, wx.EXPAND, 5 )


		bSizer6.Add( bSizer12, 1, wx.EXPAND, 5 )


		fgSizer7.Add( bSizer6, 1, wx.EXPAND, 5 )


		self.SetSizer( fgSizer7 )
		self.Layout()

		self.Centre( wx.BOTH )

		# Connect Events
		self.public_key_input.Bind( wx.EVT_FILEPICKER_CHANGED, self.extract_rsa_parameters_from_pk )
		self.encrypt.Bind( wx.EVT_BUTTON, self.encrypt_file )
		self.private_key_input.Bind( wx.EVT_FILEPICKER_CHANGED, self.extract_rsa_parameters_from_pr )
		self.decrypt.Bind( wx.EVT_BUTTON, self.decrypt_file )
		self.generate_key.Bind( wx.EVT_BUTTON, self.generate_key_pair )

	def __del__( self ):
		pass


	# Virtual event handlers, override them in your derived class

	# 公钥信息提取
	def extract_rsa_parameters_from_pk(self, event):
		# 从命名为public_key_input的控件(也就是公钥的文件选择器)获取文件的路径
		file_path = self.public_key_input.GetPath()
		# 提取公钥的模数和指数
		public_key = RSA.import_key(open(file_path).read())
		modulus_n = public_key.n
		public_exponent_e = public_key.e

		return modulus_n, public_exponent_e

	def encrypt_file(self, event):
		# 从输入明文的文件选择器获取输入文件路径
		input_file_path = self.input_file_e.GetPath()
		# 从加密部分的密文文件选择器获取输出文件路径
		output_file_path = self.output_file_e.GetPath()
		# 获取公钥参数,是一个含有模数和指数的元组
		public_key_params = self.extract_rsa_parameters_from_pk(event)
		# 获取到相关参数之后,再写一个真正进行加密运算的函数,完成加密功能
		self.encrypt_file_internal(input_file_path, output_file_path, public_key_params)


	# 加密运算
	def encrypt_file_internal(self, input_file, output_file, public_key_params):
		# 获取模数和指数
		modulus_n, public_exponent_e = public_key_params
		# 规定分组的大小,我选择的是255,理论上只要分组不大于密钥的大小就好,我用的是2048位的密钥(256B)
		CHUNK_SIZE = 255
		with open(input_file, 'rb') as in_file, open(output_file, 'wb') as out_file:
			while True:
				chunk = in_file.read(CHUNK_SIZE)
				if not chunk:
					break
				# 将每个分组的字节流转换成大整数
				d_data = int.from_bytes(chunk, byteorder='big')
				# 得到加密之后的大整数
				ciphertext_number = self.square_and_multiply(d_data, public_exponent_e, modulus_n)
				# 把密文大整数换成字节流写入文件
				ciphertext = self.int_to_bytes(ciphertext_number)
				# 把每个分组经过加密之后的密文长度保存到文件中,使得解密的时候可以按照正确的长度解密,保证无乱码
				out_file.write(len(ciphertext).to_bytes(4, byteorder='big'))
				out_file.write(ciphertext)
				# 更新文本框内容,在可视化界面提示加密已经完成
				self.feedback.SetLabel("加密完成")

	# 提取私钥参数
	def extract_rsa_parameters_from_pr(self, event):
		# 从私钥文件选择器获取文件路径
		file_path = self.private_key_input.GetPath()
		# 从 PEM 文件中提取私钥对象
		private_key = RSA.import_key(open(file_path).read())

		# 提取关键参数
		modulus_n = private_key.n
		private_exponent_d = private_key.d

		return modulus_n, private_exponent_d

	def decrypt_file(self, event):
		# 获取输入文件路径
		input_file_path = self.input_file_d.GetPath()
		# 获取输出文件路径
		output_file_path = self.output_file_d.GetPath()
		# 获取私钥参数
		private_key_params = self.extract_rsa_parameters_from_pr(event)
		# 进行解密运算
		self.decrypt_file_internal(input_file_path, output_file_path, private_key_params)

	def decrypt_file_internal(self, input_file, output_file, private_key_params):
		modulus_n, private_exponent_d = private_key_params
		# 用于存放每个分组解密之后的大整数
		plaintext_numbers = []

		with open(input_file, 'rb') as in_file:
			while True:
				# 先读取每个分组的长度
				length_bytes = in_file.read(4)
				if not length_bytes:
					break
				length = int.from_bytes(length_bytes, byteorder='big')
				# 按照分组的长度来解密
				ciphertext = in_file.read(length)
				plaintext_number = self.square_and_multiply(int.from_bytes(ciphertext, byteorder='big'),
															private_exponent_d, modulus_n)
				# 把每个分组的大整数拼接到一起,进行整体的大整数转换成字节流,不然会出现“一个中文字占多个字节,在分组的时候一个中文字被
				# 切割,要是一个大整数一个大整数的转换会有乱码“的问题,所以一定需要把所有分组的大整数拼接到一起进行转换
				plaintext_numbers.append(plaintext_number)

		concatenated_plaintext = int.from_bytes(b"".join(self.int_to_bytes(p) for p in plaintext_numbers),
												byteorder='big')

		with open(output_file, 'wb') as out_file:
			out_file.write(self.int_to_bytes(concatenated_plaintext))

		# 读取解密后的文件内容
		with open(output_file, 'rb') as decrypted_file:
			decrypted_content = decrypted_file.read().decode('utf-8')

		# 设置文本框内容为解密后的文件内容
		self.feedback.SetLabel(f"解密完成: {decrypted_content}")

	# 生成密钥对,并形成一个pr.pem的私钥文件和pk.pem公钥文件
	def generate_key_pair(self, key_size):
		key_size = 2048
		key = RSA.generate(key_size)
		private_key = key.export_key()
		public_key = key.publickey().export_key()
		with open('pr.pem', 'wb') as file:
			file.write(private_key)
		with open('pk.pem', 'wb') as file:
			file.write(public_key)

		# 更新文本框内容,用于提示
		self.feedback.SetLabel("密钥已生成")
		# 其实根据代码的意思,这个返回获取并不需要,因为解析密钥的时候都是利用pem文件,并不是这里的密钥对象,但是鉴于整个系统可以正常运行,还是
		# 不随便进行改动了
		return private_key, public_key


	# RSA加密的核心,大指数的取模运算,用平方乘算法来加速
	def square_and_multiply(self, base, exponent, modulus):
		result = 1
		base = base % modulus

		while exponent > 0:
			# 如果指数为奇数,乘以当前的base
			if exponent % 2 == 1:
				result = (result * base) % modulus

			# 将指数减半,base平方
			exponent = exponent // 2
			base = (base ** 2) % modulus

		return result

	# 辅助函数,将大整数转换为字节流
	def int_to_bytes(self, integer):
		byte_stream = integer.to_bytes((integer.bit_length() + 7) // 8, byteorder='big')
		return byte_stream


# 主函数
if __name__ == "__main__":
	# 创建一个应用程序对象
	app = wx.App()

	# 创建Frame类的实例
	frame = Frame(None)

	# 显示窗口
	frame.Show()

	# 进入主循环
	app.MainLoop()
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值