如何使用RSA 對數(shù)據(jù)加解密和簽名驗簽?一篇文章帶你搞定
回復(fù)“書籍”即可獲贈Python從入門到進(jìn)階共10本電子書

前言
加密是指利用某個值(密鑰)對明文的數(shù)據(jù)通過一定的算法變換加密(密文)數(shù)據(jù)的過程,它的逆向過程叫解密。
業(yè)務(wù)場景:一般情況下,互聯(lián)網(wǎng)上流動的數(shù)據(jù)不會被加密,無法避免這些數(shù)據(jù)泄露竊取,實際數(shù)據(jù)上傳過程中,為了保證數(shù)據(jù)不被泄露、實現(xiàn)安全數(shù)據(jù)傳輸,出現(xiàn)了各種加密技術(shù),本次主要分享如何通過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ā)送者身份,存在報文非法修改風(fēng)險,為了區(qū)分發(fā)送者的身份,那么這個時候我們就要用到簽名。
簽名原理:對報文做摘要,能防止被篡改。發(fā)送方對報文原文做加鹽hash摘要,把加密原文和摘要一起發(fā)送給接收方,接收方解密后,用同樣的hash方法計算并比對摘要,就能判斷原文是否被篡改。
簽名過程:發(fā)送者S同樣也生成了一對秘鑰,事先將公鑰給到R,在發(fā)送消息之前,先用R給的公鑰對報文加密,然后簽名使用S自己私鑰來簽名,最后將加密的消息和簽名一起發(fā)過去給R,接受者R在接收到發(fā)送者S發(fā)送的數(shù)據(jù)后,首先使用S的公鑰對簽名信息進(jìn)行驗簽,確認(rèn)身份信息,如果確認(rèn)是發(fā)送者S,然后再R才利用私鑰對加密消息進(jìn)行解密,從而隔離非法數(shù)據(jù)包的接收。
這樣一來,發(fā)送過程信息被獲取,沒有R的私鑰無法解密信息,即使獲取到發(fā)送者S的公鑰,想要仿造發(fā)送信息沒有S的私鑰無法簽名,同理R給S回復(fù)信息時,可以通過R的公鑰加密,自己的私鑰生成簽名,S接收到數(shù)據(jù)使用同樣的方式進(jìn)行解密驗證身份。私鑰加簽,公鑰驗簽,這樣就能確保只有私鑰持有者也就是發(fā)送者能合法發(fā)送數(shù)據(jù)。
加簽:

驗簽:

Python實現(xiàn)RSA加解密相關(guān)知識要點
1、首先安裝加密庫:pip install?pycryptodome
python中要使用到crypto相關(guān)的庫,使用的第三方庫是 pycryptodome,其為pycrypto的延伸版本。
rsa文檔地址:https://stuvel.eu/files/python-rsa-doc/index.html pycryptodome文檔地址:https://www.pycryptodome.org/en/latest/src/cipher/classic.html
2、字符串和Bytes互相轉(zhuǎn)換使用encode()和decode()方法
加密與解密操作的時候,要確保我們操作的數(shù)據(jù)類型是Bytes。
秘鑰對一般由密鑰簽發(fā)者來生成提供。通過RSA校驗生成自己的公鑰,私鑰,這里生成固定的備用。
from?Crypto?import?Random
from?Crypto.PublicKey?import?RSA
#?偽隨機(jī)數(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ù)進(jìn)行分段加密;為保證更安全,盡量使用 2048bit ,最多只能加密245byte 長度的數(shù)據(jù)。
秘鑰對生成如下格式:
公鑰
-----BEGIN?PUBLIC?KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtas/LB41SKhFtu49b7TZ
oiTQ+ABoT6b8REs8PuuE5PZHByA/IXvEGrNl5osOV79OMmgAxiZGirXOfWZMoZuB
7Htu97Fyyh9fc9WLlkaCPbkN6LDp6EEiW+seQbcmJHRSgQUyyzu/T9/x9aA2aoHv
JeRO59kDmYfyoyg5+rFfgzy+YizZWqFRTLV9ZXKecIfAy7Opt3rnc2EaiFDr7/zw
KJmIxOfESwmhmRV1RyMj3O0yFD1xnkZ7ouh+4OExwNjTdUVpoQDz2HaU9QC/tEIX
TyJX36sJSyciLb+8itcfhegnBiNxRYZtwzsq8o9ASBcuXjzZPU7zlVGQTxIK7lWX
PwIDAQAB
-----END?PUBLIC?KEY-----
私鑰
-----BEGIN?RSA?PRIVATE?KEY-----
MIIEpAIBAAKCAQEAtas/LB41SKhFtu49b7TZoiTQ+ABoT6b8REs8PuuE5PZHByA/
IXvEGrNl5osOV79OMmgAxiZGirXOfWZMoZuB7Htu97Fyyh9fc9WLlkaCPbkN6LDp
6EEiW+seQbcmJHRSgQUyyzu/T9/x9aA2aoHvJeRO59kDmYfyoyg5+rFfgzy+YizZ
WqFRTLV9ZXKecIfAy7Opt3rnc2EaiFDr7/zwKJmIxOfESwmhmRV1RyMj3O0yFD1x
nkZ7ouh+4OExwNjTdUVpoQDz2HaU9QC/tEIXTyJX36sJSyciLb+8itcfhegnBiNx
RYZtwzsq8o9ASBcuXjzZPU7zlVGQTxIK7lWXPwIDAQABAoIBAAJSmhN20wItGi/g
dBgH05mympqPhF9/iFpZLRm2CiduDKXT+dEN4FZ7dH74Yfd2O/1oZGYkJ1YiEYdI
T3K1GcalV1uPjbx1OG1Mzb5fA86rnVI1yaMAcWK3RIl2wLD9Ob02wBnM5EFhrbOQ
8YRc4x4nzc7bo/BxEzjJzJMrdGF2cYeVMlASxA7IE09W+FFvPAWLR1nlfx6GoZuJ
edvQtrDxgRPLRluGWRdHmASoYHgONBJmV9NjLeOyGPwK32obdeE2vQLfaghDRvmI
GJ7LAc0yYRS7Aa82/BF8/x/r/BR3o7+bFg1t3n+SFXx8eCUSsBCKL3BNu1HiyWWj
2LGFlp0CgYEAyvC/CThHuAocIp4p7nyB6Wwx0fxxgLvcUtIfP77yOZDm3T2UK6BX
gLEMb7Fpmo6uH39ewubQtSxMrJH20DUA7MyRVJQUiM+bSvJ4jG/T+I1bi7BfVuL9
QDbW+zQvQ9CHZ0ZaijelolSBzMfO6/l4km6zgeM/Wf3ypkvvX+k9VfMCgYEA5SrA
u+rnUpPb1vCBZ+DZrPkA0hYB+N+eZ0wmrM4m0dP6xI084UpU0PEdL+o8FE9azlN7
l7tWtdmx003jy3pnzr6DMMIfgB92bHCfMMPq7ddSbfz6ul/kr67oUtMAdcy+odrY
Ah59+q9oyIeVZLtBKvlzaTp6httwm/kKL2n0UIUCgYEAhFz7rM7JcE8fxLB2Vvdc
YFvSLszBVx6weFBWU2R+Zm+NNHXqg33kNKrFmsATSdyP0zlnHCYhsFlBdTkKywgX
H1vZ2llu/0CxX/PADpENp0rDj9usg2Yvmcdq9pM11LxY5FIt0YK0BKmrs14LJzwi
mRec+zW150NMFYznhx4AhGMCgYAnjF9CjuFo4Nd5mnvan3UxYq9/kgi5GG5PyVaL
T/BnGbwXG4C8KIXGoTW2RSglISS8oq+bmdr2+yCzJKgBP5iWl04wpe+lvshDIpR2
Z/ktHpG9JYFnlJD0uKyjToKv0au8ZvYMN5LqJkdhA/UGM0Kl1fLS4CKxD0G5yRq2
4AQnuQKBgQDEyPS12CtCkOZMCwoONWGlnhsTmwxZJpVbCGmFucpuJagQiIIpHnJq
3LlU9Z3wh09kGFHJlSSzdMkHzZ4/x6ra4zuGObClHju6v3I5sY3/iPw97zDTiq0P
x7f9hR//cY9wnhjbaQkpvNooHXTHL3PZC8AN4Ud+aKzzbwFWrUBYxw==
-----END?RSA?PRIVATE?KEY-----計算公式如下:秘鑰長度/8-11 = 最大加密量(單位:byte)

4、Base64編碼
base64 是網(wǎng)絡(luò)上最常見的用于傳輸8bit字節(jié)代碼的編碼方式之一,是一種基于64個可見字符來表示二進(jìn)制數(shù)據(jù)的方法。用來將非ASCII字符數(shù)據(jù)轉(zhuǎn)換成ASCII字符的一種方法,特別適合在HTTP協(xié)議下快速傳輸數(shù)據(jù)。比如郵件,ASCII 控制字符 、中文、圖片二進(jìn)制數(shù)據(jù)等。
基本原理
?base64 將 ASCII 碼 或者二進(jìn)制編碼成只包含 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é)前補(bǔ)兩個0,形成4個8字節(jié)形式。
編碼規(guī)則
base64要求把每3個8bit字節(jié)轉(zhuǎn)換為4個6bit的字節(jié),然后把6bit的字節(jié)高兩位添加為0,組成4個8bit的字節(jié),理論上將比原來長1/3。如果要編碼的二進(jìn)制數(shù)據(jù)不是3的倍數(shù),數(shù)據(jù)長度除以3的余數(shù)就是2或者1,轉(zhuǎn)換的時候結(jié)果不足6位的,用0來填充,之后在6位前面補(bǔ)兩個0,轉(zhuǎn)換完空出的結(jié)果用 = 來補(bǔ)位,最后保證編碼出來的字節(jié)為4的倍數(shù)。解碼的時候,等號會自動被去掉。
注:由于標(biāo)準(zhǔn)的Base64編碼后可能出現(xiàn)字符+和斜扛/,+和/在URL中不能直接作為參數(shù),因此,Base64提供了urlsafe_b64encode方法將+和/分別轉(zhuǎn)換為橫杠-和下畫線_,使用urlsafe_b64decode方法將橫杠-和下畫線_還原為字符+和斜扛/。
Python實現(xiàn)RSA加解密和簽名驗簽類
本文將RSA加密方法寫成一個類,支持包含中文的長字符串分段加解密。
注:經(jīng)過分段加密的數(shù)據(jù),在進(jìn)行解密的時候我們也要分成多段解密,然后解密之后再進(jìn)行拼接成原串,加密簽名與解密驗簽注意保持原串一致。
from?Crypto?import?Random
from?Crypto.PublicKey?import?RSA
from?Crypto.Cipher?import?PKCS1_v1_5?as?Cipher_PKCS1_v1_5
import?base64
from?Crypto.Signature?import?PKCS1_v1_5?as?Sig_pk
from?Crypto.Hash?import?SHA
class?RSACipher():
????'''
????RSA加密、解密、簽名、驗簽工具類
????備注:# RSA的加密機(jī)制有兩種方案一個是RSAES-OAEP,另一個RSAES-PKCS1-v1_5
????????#?對同樣的數(shù)據(jù),用同樣的key進(jìn)行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)
????????#?私鑰進(jìn)行簽名
????????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)容進(jìn)行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)建兩套公鑰私鑰、測試文本即可,快動手試一下吧。
小伙伴們,快快用實踐一下吧!如果在學(xué)習(xí)過程中,有遇到任何問題,歡迎加我好友,我拉你進(jìn)Python學(xué)習(xí)交流群共同探討學(xué)習(xí)。
-------------------?End?-------------------
往期精彩文章推薦:

歡迎大家點贊,留言,轉(zhuǎn)發(fā),轉(zhuǎn)載,感謝大家的相伴與支持
想加入Python學(xué)習(xí)群請在后臺回復(fù)【入群】
萬水千山總是情,點個【在看】行不行
/今日留言主題/
隨便說一兩句吧~

