如何使用RSA 對數(shù)據(jù)加解密和簽名驗簽?一篇文章帶你搞定

情感導師 5772

 添加導師微信MurieL0304

獲取更多愛情挽回攻略 婚姻修復技巧 戀愛脫單干貨

「來源: |Python爬蟲與數(shù)據(jù)挖掘 ID:crawler_python」

回復“書籍”即可獲贈Python從入門到進階共10本電子書

如何使用RSA 對數(shù)據(jù)加解密和簽名驗簽?一篇文章帶你搞定

三分割據(jù)紆籌策,萬古云霄一羽毛。

前言

加密是指利用某個值(密鑰)對明文的數(shù)據(jù)通過一定的算法變換加密(密文)數(shù)據(jù)的過程,它的逆向過程叫解密。

業(yè)務場景:一般情況下,互聯(lián)網(wǎng)上流動的數(shù)據(jù)不會被加密,無法避免這些數(shù)據(jù)泄露竊取,實際數(shù)據(jù)上傳過程中,為了保證數(shù)據(jù)不被泄露、實現(xiàn)安全數(shù)據(jù)傳輸,出現(xiàn)了各種加密技術,本次主要分享如何通過python來實現(xiàn)非對稱加密算法RSA加解密。

RSA算法簡介

加密和解密使用相同的密鑰叫對稱加密方式,而非對稱加密算法(公鑰加密方式)RSA加密與解密分別用不同的密鑰,公鑰用于加密,私鑰用于解密。

比如發(fā)送者S要給接受者R傳輸報文信息,為了避免信息泄露,秘鑰簽發(fā)者R事先通過RSA加密算法生成秘鑰對,并且將公鑰事先給到S,私鑰則自己保留,S向R傳輸信息時,先用R提供的公鑰加密報文,然后再將報文傳輸給R,R獲取加密后的信息,再通過其單獨持有的私鑰解密報文,即使報文被竊聽,竊聽者沒有私鑰,也無法解密。公鑰對外公開的,私鑰自己保留,由于公鑰是公開的,任何人都能拿到(會同時給到多個人),都可以使用公鑰來加密發(fā)送偽造內(nèi)容,因此,驗證發(fā)送者的身份,確保報文的安全性顯得非常重要。

考慮到一種情況:發(fā)送者S獲取接收者R的公鑰時,被中間人A獲取到了這個公鑰,通過公鑰對信息加密,冒充S來給R發(fā)篡改的報文,同樣R接受信息后也能夠通過持有的私鑰解密報文數(shù)據(jù),接受者無法辨別數(shù)據(jù)發(fā)送者身份,存在報文非法修改風險,為了區(qū)分發(fā)送者的身份,那么這個時候我們就要用到簽名。

簽名原理:對報文做摘要,能防止被篡改。發(fā)送方對報文原文做加鹽hash摘要,把加密原文和摘要一起發(fā)送給接收方,接收方解密后,用同樣的hash方法計算并比對摘要,就能判斷原文是否被篡改。

簽名過程:發(fā)送者S同樣也生成了一對秘鑰,事先將公鑰給到R,在發(fā)送消息之前,先用R給的公鑰對報文加密,然后簽名使用S自己私鑰來簽名,最后將加密的消息和簽名一起發(fā)過去給R,接受者R在接收到發(fā)送者S發(fā)送的數(shù)據(jù)后,首先使用S的公鑰對簽名信息進行驗簽,確認身份信息,如果確認是發(fā)送者S,然后再R才利用私鑰對加密消息進行解密,從而隔離非法數(shù)據(jù)包的接收。

這樣一來,發(fā)送過程信息被獲取,沒有R的私鑰無法解密信息,即使獲取到發(fā)送者S的公鑰,想要仿造發(fā)送信息沒有S的私鑰無法簽名,同理R給S回復信息時,可以通過R的公鑰加密,自己的私鑰生成簽名,S接收到數(shù)據(jù)使用同樣的方式進行解密驗證身份。私鑰加簽,公鑰驗簽,這樣就能確保只有私鑰持有者也就是發(fā)送者能合法發(fā)送數(shù)據(jù)。

加簽:

驗簽:

Python實現(xiàn)RSA加解密相關知識要點

1、首先安裝加密庫:pip install pycryptodome

python中要使用到crypto相關的庫,使用的第三方庫是 pycryptodome,其為pycrypto的延伸版本。

rsa文檔地址:https://stuvel.eu/files/python-rsa-doc/index.htmlpycryptodome文檔地址:https://www.pycryptodome.org/en/latest/src/cipher/classic.html2、字符串和Bytes互相轉(zhuǎn)換使用encode()和decode()方法

加密與解密操作的時候,要確保我們操作的數(shù)據(jù)類型是Bytes。

3、生成秘鑰對

秘鑰對一般由密鑰簽發(fā)者來生成提供。通過RSA校驗生成自己的公鑰,私鑰,這里生成固定的備用。

from Crypto import Randomfrom Crypto.PublicKey import RSA# 偽隨機數(shù)生成器random_gen = Random.new().read# 生成秘鑰對實例對象:2048是秘鑰的長度rsa = RSA.generate(2048, random_gen)# 獲取私鑰,保存到文件private_pem = rsa.exportKey()with open('private.pem', 'wb') as f: f.write(private_pem)# 獲取公鑰保存到文件public_pem = rsa.publickey().exportKey()with open('public.pem', 'wb') as f: f.write(public_pem)

生成秘鑰對的時候,可以指定生成秘鑰的長度,一般推薦使用 1024bit, 1024bit 的 rsa 公鑰,加密數(shù)據(jù)時,最多只能加密 117byte 的數(shù)據(jù),數(shù)據(jù)量超過這個數(shù),則需要對數(shù)據(jù)進行分段加密;為保證更安全,盡量使用 2048bit ,最多只能加密245byte 長度的數(shù)據(jù)。

秘鑰對生成如下格式:

公鑰-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtas/LB41SKhFtu49b7TZoiTQ+ABoT6b8REs8PuuE5PZHByA/IXvEGrNl5osOV79OMmgAxiZGirXOfWZMoZuB7Htu97Fyyh9fc9WLlkaCPbkN6LDp6EEiW+seQbcmJHRSgQUyyzu/T9/x9aA2aoHvJeRO59kDmYfyoyg5+rFfgzy+YizZWqFRTLV9ZXKecIfAy7Opt3rnc2EaiFDr7/zwKJmIxOfESwmhmRV1RyMj3O0yFD1xnkZ7ouh+4OExwNjTdUVpoQDz2HaU9QC/tEIXTyJX36sJSyciLb+8itcfhegnBiNxRYZtwzsq8o9ASBcuXjzZPU7zlVGQTxIK7lWXPwIDAQAB-----END PUBLIC KEY-----私鑰-----BEGIN RSA PRIVATE KEY-----MIIEpAIBAAKCAQEAtas/LB41SKhFtu49b7TZoiTQ+ABoT6b8REs8PuuE5PZHByA/IXvEGrNl5osOV79OMmgAxiZGirXOfWZMoZuB7Htu97Fyyh9fc9WLlkaCPbkN6LDp6EEiW+seQbcmJHRSgQUyyzu/T9/x9aA2aoHvJeRO59kDmYfyoyg5+rFfgzy+YizZWqFRTLV9ZXKecIfAy7Opt3rnc2EaiFDr7/zwKJmIxOfESwmhmRV1RyMj3O0yFD1xnkZ7ouh+4OExwNjTdUVpoQDz2HaU9QC/tEIXTyJX36sJSyciLb+8itcfhegnBiNxRYZtwzsq8o9ASBcuXjzZPU7zlVGQTxIK7lWXPwIDAQABAoIBAAJSmhN20wItGi/gdBgH05mympqPhF9/iFpZLRm2CiduDKXT+dEN4FZ7dH74Yfd2O/1oZGYkJ1YiEYdIT3K1GcalV1uPjbx1OG1Mzb5fA86rnVI1yaMAcWK3RIl2wLD9Ob02wBnM5EFhrbOQ8YRc4x4nzc7bo/BxEzjJzJMrdGF2cYeVMlASxA7IE09W+FFvPAWLR1nlfx6GoZuJedvQtrDxgRPLRluGWRdHmASoYHgONBJmV9NjLeOyGPwK32obdeE2vQLfaghDRvmIGJ7LAc0yYRS7Aa82/BF8/x/r/BR3o7+bFg1t3n+SFXx8eCUSsBCKL3BNu1HiyWWj2LGFlp0CgYEAyvC/CThHuAocIp4p7nyB6Wwx0fxxgLvcUtIfP77yOZDm3T2UK6BXgLEMb7Fpmo6uH39ewubQtSxMrJH20DUA7MyRVJQUiM+bSvJ4jG/T+I1bi7BfVuL9QDbW+zQvQ9CHZ0ZaijelolSBzMfO6/l4km6zgeM/Wf3ypkvvX+k9VfMCgYEA5SrAu+rnUpPb1vCBZ+DZrPkA0hYB+N+eZ0wmrM4m0dP6xI084UpU0PEdL+o8FE9azlN7l7tWtdmx003jy3pnzr6DMMIfgB92bHCfMMPq7ddSbfz6ul/kr67oUtMAdcy+odrYAh59+q9oyIeVZLtBKvlzaTp6httwm/kKL2n0UIUCgYEAhFz7rM7JcE8fxLB2VvdcYFvSLszBVx6weFBWU2R+Zm+NNHXqg33kNKrFmsATSdyP0zlnHCYhsFlBdTkKywgXH1vZ2llu/0CxX/PADpENp0rDj9usg2Yvmcdq9pM11LxY5FIt0YK0BKmrs14LJzwimRec+zW150NMFYznhx4AhGMCgYAnjF9CjuFo4Nd5mnvan3UxYq9/kgi5GG5PyVaLT/BnGbwXG4C8KIXGoTW2RSglISS8oq+bmdr2+yCzJKgBP5iWl04wpe+lvshDIpR2Z/ktHpG9JYFnlJD0uKyjToKv0au8ZvYMN5LqJkdhA/UGM0Kl1fLS4CKxD0G5yRq24AQnuQKBgQDEyPS12CtCkOZMCwoONWGlnhsTmwxZJpVbCGmFucpuJagQiIIpHnJq3LlU9Z3wh09kGFHJlSSzdMkHzZ4/x6ra4zuGObClHju6v3I5sY3/iPw97zDTiq0Px7f9hR//cY9wnhjbaQkpvNooHXTHL3PZC8AN4Ud+aKzzbwFWrUBYxw==-----END RSA PRIVATE KEY-----

計算公式如下:秘鑰長度/8-11 = 最大加密量(單位:byte)

4、Base64編碼

base64 是網(wǎng)絡上最常見的用于傳輸8bit字節(jié)代碼的編碼方式之一,是一種基于64個可見字符來表示二進制數(shù)據(jù)的方法。用來將非ASCII字符數(shù)據(jù)轉(zhuǎn)換成ASCII字符的一種方法,特別適合在HTTP協(xié)議下快速傳輸數(shù)據(jù)。比如郵件,ASCII 控制字符 、中文、圖片二進制數(shù)據(jù)等。

基本原理

base64 將 ASCII 碼 或者二進制編碼成只包含 A~Z、a~z、0~9、+ 、/ 這64個字符(26個大寫字符、26個小寫字符、10個數(shù)字、+/)。通過3個8bit字節(jié)( 3 x 8 = 24 )編碼成4個6位字節(jié)(4 x 6 = 24),在每個6位字節(jié)前補兩個0,形成4個8字節(jié)形式。

編碼規(guī)則

base64要求把每3個8bit字節(jié)轉(zhuǎn)換為4個6bit的字節(jié),然后把6bit的字節(jié)高兩位添加為0,組成4個8bit的字節(jié),理論上將比原來長1/3。如果要編碼的二進制數(shù)據(jù)不是3的倍數(shù),數(shù)據(jù)長度除以3的余數(shù)就是2或者1,轉(zhuǎn)換的時候結(jié)果不足6位的,用0來填充,之后在6位前面補兩個0,轉(zhuǎn)換完空出的結(jié)果用 = 來補位,最后保證編碼出來的字節(jié)為4的倍數(shù)。解碼的時候,等號會自動被去掉。

注:由于標準的Base64編碼后可能出現(xiàn)字符+和斜扛/,+和/在URL中不能直接作為參數(shù),因此,Base64提供了urlsafe_b64encode方法將+和/分別轉(zhuǎn)換為橫杠-和下畫線_,使用urlsafe_b64decode方法將橫杠-和下畫線_還原為字符+和斜扛/。

Python實現(xiàn)RSA加解密和簽名驗簽類

本文將RSA加密方法寫成一個類,支持包含中文的長字符串分段加解密。

注:經(jīng)過分段加密的數(shù)據(jù),在進行解密的時候我們也要分成多段解密,然后解密之后再進行拼接成原串,加密簽名與解密驗簽注意保持原串一致。

from Crypto import Randomfrom Crypto.PublicKey import RSAfrom Crypto.Cipher import PKCS1_v1_5 as Cipher_PKCS1_v1_5import base64from Crypto.Signature import PKCS1_v1_5 as Sig_pkfrom Crypto.Hash import SHAclass RSACipher():''' RSA加密、解密、簽名、驗簽工具類 備注:# RSA的加密機制有兩種方案一個是RSAES-OAEP,另一個RSAES-PKCS1-v1_5 # 對同樣的數(shù)據(jù),用同樣的key進行RSA加密, 每次的輸出都會不一樣;但是這些加密的結(jié)果都能正確的解密 ''' def read_xml(self,xmlfile):''' 讀取待加密明文方法 ''' with open(xmlfile, 'r', encoding="utf-8") as file:# 用open()將XML文件中的內(nèi)容讀取為字符串 xmlstr = file.read()print(xmlstr)return xmlstr def encrypt_file(self,encrypt_file):''' 保存加密后密文方法 ''' with open(encrypt_file, 'rb') as f: message = f.read()return message def Encrypt(self, message, publicKeyfile, out_file):''' 加密方法 :param message: 需要加密的明文 :param publicKeyfile: 公鑰文件 :param out_file: 輸出密文 :return: 加密后的文本 ''' with open(publicKeyfile, 'r') as f: publicKey = f.read() pubKey = RSA.importKey(publicKey) cipher = Cipher_PKCS1_v1_5.new(pubKey) message = message.encode()# 分段加密,加密長度byte為8的倍數(shù),最長不超出最大加密量(單位:byte)=秘鑰長度/8-11 length = len(message) default_length = 245 offset = 0 res = bytes()while length - offset > 0:if length - offset > default_length: _res = cipher.encrypt(message[offset:offset + default_length])else: _res = cipher.encrypt(message[offset:]) offset += default_length res += _res encrypt_text=base64.b64encode(res) with open(out_file, 'wb') as f_w: f_w.write(base64.b64encode(res))return encrypt_text def Decrypt(self,message, privateKeyfile, out_file):''' 解密方法 :param message: 加密后的密文 :param privateKey: 私鑰文件 :param out_file: 輸出明文 :return: 解密后的文本 ''' with open(privateKeyfile, 'r') as f: privateKey = f.read() rsaKey = RSA.importKey(privateKey) cipher = Cipher_PKCS1_v1_5.new(rsaKey) randomGenerator = Random.new().read message = base64.b64decode(message.decode()) res = []for i in range(0, len(message), 256): res.append(cipher.decrypt((message[i:i + 256]),randomGenerator)) plainText = bytes(b"".join(res)).decode()print(plainText) with open(out_file, 'w', encoding='utf-8') as f_w: f_w.write(plainText)return plainText def sign(self,message, private_sign_file):''' 簽名方法 :param message: 需要簽名的文本 :param private_sign_file: 私鑰文件 :return: 簽名信息 ''' with open(private_sign_file, 'r') as f: private_sign = f.read() message = message.encode() private_key = RSA.importKey(private_sign)# 根據(jù)sha算法處理簽名內(nèi)容 hash_value = SHA.new(message)# 私鑰進行簽名 signer = Sig_pk.new(private_key) signature = signer.sign(hash_value) result=base64.b64encode(signature).decode()return result # 將簽名后的內(nèi)容,轉(zhuǎn)換為base64編碼 def verify(self,message,public_sign_file,signature):''' 驗簽方法 :param message: 需要驗簽的文本 :param public_sign_file: 公鑰文件 :param signature: 簽名信息 :return: 驗簽結(jié)果 ''' with open(public_sign_file, 'r') as f: public_sign = f.read() signature = base64.b64decode(signature)# 將簽名之前的內(nèi)容進行hash處理 public_key = RSA.importKey(public_sign)print(public_key)# 驗證簽名 hash_value = SHA.new(message.encode()) verifier = Sig_pk.new(public_key)return verifier.verify(hash_value, signature)if __name__ == '__main__':#創(chuàng)建RSA加密實例 rsacipher = RSACipher() xmlfile = r'new1.xml' message=rsacipher.read_xml(xmlfile) #待加密明文 encryptFile = "encrypt.txt"#加密后密文 publicKeyfile="rsa.pub"#公鑰加密# 加密 encrypt_text = rsacipher.Encrypt(message,publicKeyfile,encryptFile)print('加密后:\n%s' % encrypt_text)# 簽名 private_sign_file="private.pem"# 私鑰簽名 signature = rsacipher.sign(message,private_sign_file)print('簽名:\n%s' % signature)# 解密 decryptFile ="deencrypt.txt"#輸出解密內(nèi)容 privateFile = "rsa.key"#私鑰解密 decrypt_text = rsacipher.Decrypt(encrypt_text,privateFile,decryptFile)print('解密后:\n%s' % decrypt_text)# 驗簽 pubic_sign_file = "public.pem"# 公鑰驗簽 result = rsacipher.verify(decrypt_text,pubic_sign_file,signature)print('驗簽:\n%s' % result)

文中包含所有源碼,自己動手創(chuàng)建兩套公鑰私鑰、測試文本即可,快動手試一下吧。

-------------------End -------------------

評論列表

頭像
2024-09-03 03:09:44

挺專業(yè)的一個情感機構(gòu),我一個朋友在那里咨詢過,服務很貼心!

頭像
2024-04-25 12:04:37

如果發(fā)信息,對方就是不回復,還不刪微信怎么挽回?

頭像
2023-11-09 08:11:39

可以幫助復合嗎?

 添加導師微信MurieL0304

獲取更多愛情挽回攻略 婚姻修復技巧 戀愛脫單干貨

發(fā)表評論 (已有3條評論)