RSA非对称加密算法实现:Python
RSA是1977年由罗纳德·李维斯特(Ron Rivest)、阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)一起提出的。当时他们三人都在麻省理工学院工作。RSA就是他们三人姓氏开头字母拼在一起组成的。
RSA解决了对称加密的一个不足,比如AES算法加密和解密时使用的是同一个秘钥,因此这个秘钥不能公开,因此对于需要公开秘钥的场合,我们需要在加密和解密过程中使用不同的秘钥,加密使用的公钥可以公开,解密使用的私钥要保密,这就是非对称加密的好处。
常用的开发语言来实现RSA加密:
公钥与私钥
公钥与私钥是成对的,一般的,我们认为的是公钥加密、私钥解密、私钥签名、公钥验证,有人说成私钥加密,公钥解密时不对的。
公钥与私钥的生成有多种方式,可以通过程序生成(下文具体实现),可以通过openssl工具:
# 生成一个私钥,推荐使用1024位的秘钥,秘钥以pem格式保存到-out参数指定的文件中,采用PKCS1格式 openssl genrsa -out rsa.pem 1024 # 生成与私钥对应的公钥,生成的是Subject Public Key,一般配合PKCS8格式私钥使用 openssl rsa -in rsa.pem -pubout -out rsa.pub
RSA生成公钥与私钥一般有两种格式:PKCS1和PKCS8,上面的命令生成的秘钥是PKCS1格式的,而公钥是Subject Public Key,一般配合PKCS8格式私钥使用,所以就可能会涉及到PKCS1和PKCS8之间的转换:
# PKCS1格式私钥转换为PKCS8格式私钥,私钥直接输出到-out参数指定的文件中 openssl pkcs8 -topk8 -inform PEM -in rsa.pem -outform pem -nocrypt -out rsa_pkcs8.pem # PKCS8格式私钥转换为PKCS1格式私钥,私钥直接输出到-out参数指定的文件中 openssl rsa -in rsa_pkcs8.pem -out rsa_pkcs1.pem # PKCS1格式公钥转换为PKCS8格式公钥,转换后的内容直接输出 openssl rsa -pubin -in rsa.pub -RSAPublicKey_out # PKCS8格式公钥转换为PKCS1格式公钥,转换后的内容直接输出 openssl rsa -RSAPublicKey_in -pubout -in rsa.pub
现实中,我们往往从pem、crt、pfx文件获取公私和私钥,crt、pfx的制作可以参考:简单的制作ssl证书,并在nginx和IIS中使用,或者使用现成的:https://pan.baidu.com/s/1MJ5YmuZiLBnf-DfNR_6D7A (提取码:c6tj),密码都是:123456
Python实现
首先使用pip两个第三方包:
# 用于从crt、pfx等文件读取公私秘钥 pip install pyOpenSSL # 用RSA加密解密签名验证等(如果安装不了,先卸载旧版本再安装:pip uninstall pycrypto) pip install pycryptodome
接着封装一个模块RsaUtil:
# 需要安装OpenSSL包:pip install pyOpenSSL # 需要安装pycrypto包:pip install pycryptodome from Crypto import Random, Hash from Crypto.PublicKey import RSA from Crypto.Cipher import PKCS1_v1_5 as PKCS1_v1_5_Cipher from Crypto.Signature import PKCS1_v1_5 as PKCS1_v1_5_Signature from Crypto.IO import PEM, PKCS8 from OpenSSL import crypto from Crypto.Util.asn1 import (DerSequence, DerBitString, DerObjectId, DerNull) # pkcs1格式转换为pkcs8 def pkcs1_to_pkcs8(buffer): rsakey = RSA.importKey(buffer) if rsakey.has_private(): return PKCS8.wrap(buffer, RSA.oid, None) return rsakey.exportKey("DER") # pkcs8格式转换为pkcs1 def pkcs8_to_pkcs1(buffer): rsakey = RSA.importKey(buffer) if rsakey.has_private(): # 私钥 return PKCS8.unwrap(buffer, None)[1] spki = DerSequence().decode(buffer, nr_elements=2) algo = DerSequence().decode(spki[0], nr_elements=(1, 2)) algo_oid = DerObjectId().decode(algo[0]) spk = DerBitString().decode(spki[1]).value if len(algo) == 1: algo_params = None else: try: DerNull().decode(algo[1]) algo_params = None except: algo_params = algo[1] if algo_oid.value != RSA.oid or algo_params is not None: raise ValueError("No RSA subjectPublicKeyInfo") return spk # 生成RSA加密的公私密钥 def generate_rsa_key(use_pkcs8=False): rsa_key = RSA.generate(1024, Random.new().read) # 1024位 private_key = rsa_key.exportKey("DER", pkcs=1) public_key = rsa_key.publickey().exportKey("DER") # subject_public_key if use_pkcs8: private_key = pkcs1_to_pkcs8(private_key) else: public_key = pkcs8_to_pkcs1(public_key) return public_key, private_key # 从pem文件中读取秘钥 def read_from_pem(pen_file_ame): with open(pen_file_ame, 'r') as f: t = PEM.decode(f.read()) if t[2]: raise ValueError("fail to read pem") return t[0] # 将秘钥写入pem文件 def write_to_pem(buffer, is_private_key, pen_file_ame): if is_private_key: str = PEM.encode(buffer, "RSA PRIVATE KEY") else: str = PEM.encode(buffer, "RSA PUBLIC KEY") with open(pen_file_ame, 'w') as f: f.write(str) # 从crt文件中读取公钥 def read_public_key_from_crt(crt_file_name): with open(crt_file_name, 'rb') as f: cert = crypto.load_certificate(crypto.FILETYPE_PEM, f.read()) return crypto.dump_publickey(crypto.FILETYPE_PEM, cert.get_pubkey()) # 从pfx文件中读取公私钥 def read_from_pfx(pfx_file_name, password): with open(pfx_file_name, 'rb') as f: pfx = crypto.load_pkcs12(f.read(), bytes(password, encoding="utf-8")) cert = pfx.get_certificate() _public_key = crypto.dump_publickey(crypto.FILETYPE_PEM, cert.get_pubkey()) _privat_key = crypto.dump_privatekey(crypto.FILETYPE_PEM, pfx.get_privatekey()) return _public_key, _privat_key # RSA使用公钥加密 def rsa_encrypt(value, public_key): rsakey = RSA.importKey(public_key) cipher = PKCS1_v1_5_Cipher.new(rsakey) buffer = cipher.encrypt(value.encode("utf-8")) return buffer.hex() # 使用hex格式输出 # RSA使用私钥解密 def rsa_decrypt(value, private_key): rsakey = RSA.importKey(private_key) cipher = PKCS1_v1_5_Cipher.new(rsakey) buffer = bytes.fromhex(value) # 读取hex格式数据 buffer = cipher.decrypt(buffer, Random.new().read) return buffer.decode("utf-8") # RSA使用私钥签名 def sign(value, private_ey, halg=Hash.MD5): rsakey = RSA.importKey(private_ey) signer = PKCS1_v1_5_Signature.new(rsakey) hash = halg.new() hash.update(value.encode("utf-8")) buffer = signer.sign(hash) return buffer.hex() # 使用hex格式输出 # RSA使用公钥验证签名 def verify(value, public_key, signature, halg=Hash.MD5): rsakey = RSA.importKey(public_key) verifier = PKCS1_v1_5_Signature.new(rsakey) hash = halg.new() hash.update(value.encode("utf-8")) buffer = bytes.fromhex(signature) # 读取hex格式数据 result: bool = verifier.verify(hash, buffer) return result
生成RSA的公私秘钥:
# 生成rsa公私秘钥 (publicKey, privateKey) = RsaUtil.generate_rsa_key(use_pkcs8)
生成秘钥后,需要保存,一般保存到pem文件中:
# 将公私秘钥写入pem文件,filePath是文件目录 RsaUtil.write_to_pem(publicKey, False, os.path.join(filePath, "rsa.pub")) RsaUtil.write_to_pem(privateKey, True, os.path.join(filePath, "rsa.pem"))
可以保存到pem文件中,当然也可以从pem文件中读取了:
# 从pem文件中读取秘钥,filePath是文件目录 publicKey = RsaUtil.read_from_pem(os.path.join(filePath, "rsa.pub")) privateKey = RsaUtil.read_from_pem(os.path.join(filePath, "rsa.pem"))
还可以从crt证书中读取公钥,而crt文件不包含私钥,因此需要单独获取私钥:
# 从crt文件读取,filePath是文件目录 publicKey = RsaUtil.read_public_key_from_crt(os.path.join(filePath, "demo.crt")) privateKey = RsaUtil.read_from_pem(os.path.join(filePath, "demo.key"))
pfx文件中包含了公钥和私钥,可以很方便就读取到:
# 从demo.pfx文件读取(demo.pfx采用的是pkcs1),filePath是文件目录 (publicKey, privateKey) = RsaUtil.read_from_pfx(os.path.join(filePath, "demo.pfx"), "123456")
有时候我们还可能需要进行秘钥的转换:
# Pkcs8格式公钥转换为Pkcs1格式公钥 publicKey = RsaUtil.pkcs8_to_pkcs1(publicKey) # Pkcs8格式私钥转换为Pkcs1格式私钥 privateKey = RsaUtil.pkcs8_to_pkcs1(privateKey) # Pkcs1格式公钥转换为Pkcs8格式公钥 publicKey = RsaUtil.pkcs1_to_pkcs8(publicKey) # Pkcs1格式私钥转换为Pkcs8格式私钥 privateKey = RsaUtil.pkcs1_to_pkcs8(privateKey)
有了公钥和私钥,接下就就能实现加密、解密、签名、验证签名等操作了:
encryptText = RsaUtil.rsa_encrypt(text, publicKey) print("【", text, "】经过【RSA】加密后:", encryptText) decryptText = RsaUtil.rsa_decrypt(encryptText, privateKey) print("【", encryptText, "】经过【RSA】解密后:", decryptText) signature = RsaUtil.sign(text, privateKey, Hash.MD5) print("【", text, "】经过【RSA】签名后:", signature) result = RsaUtil.verify(text, publicKey, signature, Hash.MD5) print("【", text, "】的签名【", signature, "】经过【RSA】验证后结果是:", result)
完整的demo代码:
import RsaUtil from Crypto import Hash import os text = "上山打老虎" use_pkcs8 = True filePath = os.getcwd() print("文件路径:", filePath) # 生成rsa公私秘钥 (publicKey, privateKey) = RsaUtil.generate_rsa_key(use_pkcs8) # 从pem文件中读取秘钥,filePath是文件目录 # publicKey = RsaUtil.read_from_pem(os.path.join(filePath, "rsa.pub")) # privateKey = RsaUtil.read_from_pem(os.path.join(filePath, "rsa.pem")) # 从demo.pfx文件读取(demo.pfx采用的是pkcs1),filePath是文件目录 # (publicKey, privateKey) = RsaUtil.read_from_pfx(os.path.join(filePath, "demo.pfx"), "123456") # 从crt文件读取,filePath是文件目录 # publicKey = RsaUtil.read_public_key_from_crt(os.path.join(filePath, "demo.crt")) # privateKey = RsaUtil.read_from_pem(os.path.join(filePath, "demo.key")) # 将公私秘钥写入pem文件,filePath是文件目录 RsaUtil.write_to_pem(publicKey, False, os.path.join(filePath, "rsa.pub")) RsaUtil.write_to_pem(privateKey, True, os.path.join(filePath, "rsa.pem")) # Pkcs8格式公钥转换为Pkcs1格式公钥 publicKey = RsaUtil.pkcs8_to_pkcs1(publicKey) # Pkcs8格式私钥转换为Pkcs1格式私钥 privateKey = RsaUtil.pkcs8_to_pkcs1(privateKey) # Pkcs1格式公钥转换为Pkcs8格式公钥 publicKey = RsaUtil.pkcs1_to_pkcs8(publicKey) # Pkcs1格式私钥转换为Pkcs8格式私钥 privateKey = RsaUtil.pkcs1_to_pkcs8(privateKey) encryptText = RsaUtil.rsa_encrypt(text, publicKey) print("【", text, "】经过【RSA】加密后:", encryptText) decryptText = RsaUtil.rsa_decrypt(encryptText, privateKey) print("【", encryptText, "】经过【RSA】解密后:", decryptText) signature = RsaUtil.sign(text, privateKey, Hash.MD5) print("【", text, "】经过【RSA】签名后:", signature) result = RsaUtil.verify(text, publicKey, signature, Hash.MD5) print("【", text, "】的签名【", signature, "】经过【RSA】验证后结果是:", result)