闲来无事,用python的tkinter开发了一个基于RSA和AES混合加密的小小工具。总结一下使用到的知识点。
首先是核心的加解密部分。 采用混合加密的原因,提到这个,就得先聊下RSA和AES各自的优缺点。 RSA
优点 安全,基于大数因式分解。而目前已破解的最高位数为1024 bit,采用2048将会很安全。且仅涉及到一次密钥传输,及将公钥随加密工具传输给使用者,私钥保留于本地,公钥即使被别人获取也没有影响。
缺点
加解密速度慢。由于都是大数计算,RSA最快的情况也比AES慢100倍。(快速幂取模运算,中国剩余定理CRT之后再写总结)一次性只能加解密小于公共模数N的数据。例如我们采用utf8编码的汉字,一个字按3 bytes也就是24 bit算,一个2048 bit的RSA key最多只能加密2048/24差不多80字。如果要加密更长的数据,就要分组加密,无疑又放大了加解密速度慢这个缺点。AES
优点
加解密速度快。分组长度和密钥长度设计灵活。数据长度不限。缺点 由于是对称加密算法,加密解密使用同一把密钥,所以密钥传输过程中存在密钥被人获取的安全隐患。
所以一般采用RSA和对称加密算法结合的方式来弥补两种算法的缺点,即在保证密钥传输过程安全性的前提下,实现既快又多地加密数据。具体实现过程是:
加密时:
随机生成一个会话密钥,或者叫过程密钥用此会话密钥加密数据用RSA公钥加密会话密钥将加密的会话密钥与数据的密文拼装起来发送出去,也可以选择生成校验码一起拼装发送,以防止数据传输过程中被人篡改。解密时:
用RSA私钥解密会话密钥使用会话密钥解密数据,如果有带校验码,此过程需要进行校验。上代码 mycipher.py
from Cryptodome.Cipher import AES, PKCS1_OAEP from Cryptodome.Random import get_random_bytes from Cryptodome.PublicKey import RSA AES_KEY_SIZE = 16 BLOCK_SIZE = 16 def generateRSAkey(): ''' 生成长度为2048 bit的RSA公私钥,分别存放于两个文件中 ''' key = RSA.generate(2048) private_key = key.export_key() file_out = open("private.pem", "wb") file_out.write(private_key) file_out.close() public_key = key.publickey().export_key() file_out = open("public.pem", "wb") file_out.write(public_key) file_out.close() class mycipher(): def __init__(self, rsakey): ''' 根据参数来创建RSA加解密对象,加密时传参公钥,解密时传参私钥 ''' self.rsakey = RSA.importKey(open(rsakey).read()) self.cipher_rsa = PKCS1_OAEP.new(self.rsakey) def encrypt(self, data): if isinstance(data, str): data = data.encode('utf-8') #随机生成一把会话密钥 sessionkey = get_random_bytes(AES_KEY_SIZE) #用RSA公钥加密会话密钥 encsessionkey = self.cipher_rsa.encrypt(sessionkey) cipher_aes = AES.new(sessionkey, AES.MODE_EAX) #采用AES EAX模式加密并生成校验tag ciphertext, tag = cipher_aes.encrypt_and_digest(data) #拼装密文会话密钥+nonce+tag+数据密文,此处nonce可以理解为IV return encsessionkey + cipher_aes.nonce + tag + ciphertext def decrypt(self, data): if isinstance(data, str): data = bytes.fromhex(data) pos = 0 encsessionkey = data[pos:self.rsakey.size_in_bytes()] pos += self.rsakey.size_in_bytes() nonce = data[pos:pos + AES_KEY_SIZE] pos += AES_KEY_SIZE tag = data[pos:pos + 16] pos += 16 ciphertext = data[pos:] sessionkey = self.cipher_rsa.decrypt(encsessionkey) cipher_aes = AES.new(sessionkey, AES.MODE_EAX, nonce) plaintext = cipher_aes.decrypt_and_verify(ciphertext, tag) plaintext = plaintext.decode('utf-8') return plaintext接下去是图形界面部分,随便写的一个很丑的界面,需要总结记录的不多,直接上代码。
window.py
from tkinter import * import tkinter.font as tkFont class window(): win = None def __init__(self, title='useless tool'): self.win = Tk() self.win.title(title) def createtext(self, text=None, state=NORMAL): state = DISABLED if state == 0 else NORMAL Label(self.win, text=text).pack(anchor='w') #Enable虽然是输入框,但是只支持单行,不美观,而Text控件可支持多行输入。 e = Text(self.win, width=100, height=20, state=state) e.pack() return e def createbutton(self, text='Button', command=None): f = tkFont.Font(size=30) b = Button(self.win, text=text, command=command, width=20, height=2, font=f, background='#E6E6FA') b.pack() return b def show(self): self.win.mainloop()加密工具 此工具随RSA公钥文件public.pem一起发送给使用者。
encrypt.py
from window import window from mycipher import mycipher def encrypt(): mc = mycipher('public.pem') plaintext = plainbox.get('0.0', 'end')[:-1] if len(plaintext) > 0: ciphertext = mc.encrypt(plaintext) cipherbox.delete('0.0', 'end') cipherbox.insert('0.0', ciphertext.hex()) if __name__ == '__main__': a = window() plainbox = a.createtext('请输入要加密的内容') cipherbox = a.createtext('这是你的密文', state=1) b = a.createbutton('加密', command=encrypt) a.show()解密工具
自己留着。
from window import window from mycipher import mycipher def decrypt(): mc = mycipher('private.pem') plaintext = mc.decrypt(cipherbox.get('0.0', 'end')[:-1]) plainbox.delete('0.0', 'end') plainbox.insert('0.0', plaintext) if __name__ == '__main__': a = window() cipherbox = a.createtext('请输入要解密的内容') plainbox = a.createtext('这是你的明文', state=1) b = a.createbutton('解密', command=decrypt) a.show()