Python

Python 以 PyCryptodome 實作 RSA 非對稱式加密方法教學與範例

介紹如何在 Python 中使用 PyCryptodome 模組產生 RSA 金鑰,並對資料進行加密與解密。

Python 的 PyCryptodome 模組提供了各種加密演算法工具,其安裝方式請參考另外一篇 PyCryptodome 實作 AES 對稱式加密方法教學

產生 RSA 金鑰

在使用 RSA 加密演算法加密資料之前,要先建立一組 RSA 金鑰,以下是使用 PyCryptodome 模組產生一組 2048 位元 RSA 金鑰的範例。

from Crypto.PublicKey import RSA

# 產生 2048 位元 RSA 金鑰
key = RSA.generate(2048)

# RSA 私鑰
privateKey = key.export_key()
with open("private.pem", "wb") as f:
    f.write(privateKey)

# RSA 公鑰
publicKey = key.publickey().export_key()
with open("public.pem", "wb") as f:
    f.write(publicKey)

以下是讀取 RSA 金鑰的方法。

from Crypto.PublicKey import RSA

# 讀取 RSA 金鑰
encodedKey = open("private.pem", "rb").read()

# 解密 RSA 金鑰
key = RSA.import_key(encodedKey)

# 輸出 RSA 私鑰
print(key.export_key().decode('utf-8'))
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAl0gWgLfW8v59cKF/60CpdmvVTzFBhXiYbGtWTT5oKDSNuuvo
jN2WRDJ3WqeUbB/TfuClLF8941msCm1uf4BAp+3XcLC0T5+lmqev1JvRH4b1Hb3U
iqdhoy2t2S7l8Vtk6SOa+sTqmz2evcvf9gAu9XFWsQUCZI1FY/wdgd+e+RU81asV
D/k6jS/cHZIHgBOOVn+zDDobS/jKempH/pY+95S6jYN2F2nbGT8wxXrp6YpnPwAi
Ox+R9HpXwB4ExFQQxRsg8N7d3mlLAo7cfKTPWqSibXBGp5XY6UAkrLMYIR/gjOPI
sHNt3T2m2Pu2oPs4aVWxMphkdKzzu6Dvi/AVqwIDAQABAoIBACpXg+zdB62Vazim
Cy6mN4gsoFR8/dDEsyzNUidmizKNmwmxU7JxpshJs7xM4S7uLXz6lWB4+9JxW8TD
YkUPeYlG+aDR5HXQwkIqIv2H6x0+4cZERoPUW3CXrWv3Q2atZO4YCB2eFegDdDW9
JV8uJ/aMlW4Qb94C6GQWwY+DLnSafdChtcc1uexoxqB/PEtesMibGHDBGLPDBuL7
64t+ms7mPnyqpdTZg23yvahqKIjGjDqx2Wskj3VkijwGWTsrIX2c2cOcDZWICBou
MalazM8eKiIwqDZWV7uD4DR/PSFwviKPkuu5Lm11HlU71A2ZnW3+2ncoJEQuSvNM
ciL0pYECgYEAuu/2QY4VNXAH6f96Z7OF1NPK4KVM8HkJsxxHw7rB8B0voDV4TXtG
o5SHAyJQWHDEpSHbiR6RySIrDZNCrkNvSxH0BAPhcYOGc+3eFJb/OlyqiewY3EKJ
M8RNsyE4HYriwKIfshXSbIkS6ozBMh7g6JTEoOlSP3zt4+Bf+WSbykECgYEAzyvm
K30nCUHLo3E0LoHqZ/lO9+QF5DUJUe2YF0ZMKQncCGzg4MxEmKzz+PNDkI00Df2g
60Wyfrpxy3x4HoDxxTNytXmzHgnHDu5OgMEWwEyw0l64B4AooF+OD1fgua5CYM87
+t5mMPsFRA88xkc2QHve3Qnj27+l8+hhjq0abOsCgYB4/6FxxTnNIPq+Z3MfdIAH
xh7nUZd/f5jbqk55ToUBmqKdHH8GB+ktMJJDYQ6IcFWYXVXwZy0N92POoabjjRqY
iMJdZvWht/RrTWgWA7aoXBq2s29UAm00aLeyXqon1SH4dff2GVHelsr7rqfOfVYu
tTyguuVV2tNQUIdOseR1AQKBgQC700gNm5z9lopidyrhE2YKuAwxUKlKug7mGWvv
hgtclZTIirXi+S8j4IIc7agO8QYFvTHcvEbVAIJVA1cACNWpfTh9cYXEMQco8UN5
FhZjZ+RUBg4Att+ebqOkI38ZuPRzQs9VZbbup1ah2tK1zTBRjDyE2AeeXoW9uaxh
J22y2wKBgQCGbhFXt+dcmOoeRVNMNtg+14AbMGmr+6DHDf81ZVfj4yhoZ2K8Iwa7
J1UsINWEMBjj6LXA3ZHkxTNkB5lx+eZAkXLGWhrX5UaGZxmV1m8QvOpQReCTpACF
i4mTNhCnYOJCEyfxywrYPPXlVu9Z8uYpFsiphF/Qf0pjJTe8tHfljg==
-----END RSA PRIVATE KEY-----
# 輸出 RSA 公鑰
print(key.publickey().export_key().decode('utf-8'))
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAl0gWgLfW8v59cKF/60Cp
dmvVTzFBhXiYbGtWTT5oKDSNuuvojN2WRDJ3WqeUbB/TfuClLF8941msCm1uf4BA
p+3XcLC0T5+lmqev1JvRH4b1Hb3Uiqdhoy2t2S7l8Vtk6SOa+sTqmz2evcvf9gAu
9XFWsQUCZI1FY/wdgd+e+RU81asVD/k6jS/cHZIHgBOOVn+zDDobS/jKempH/pY+
95S6jYN2F2nbGT8wxXrp6YpnPwAiOx+R9HpXwB4ExFQQxRsg8N7d3mlLAo7cfKTP
WqSibXBGp5XY6UAkrLMYIR/gjOPIsHNt3T2m2Pu2oPs4aVWxMphkdKzzu6Dvi/AV
qwIDAQAB
-----END PUBLIC KEY-----

加密保護 RSA 金鑰

重要的 RSA 私鑰通常會再加上一層密碼保護,再將其儲存於檔案中,以降低金鑰外洩的風險。

from Crypto.PublicKey import RSA

# 保護金鑰的密碼
secretCode = "secret#code"

# 產生 2048 位元 RSA 金鑰
key = RSA.generate(2048)

# 以密碼加密保護 RSA 金鑰
encryptedKey = key.export_key(passphrase=secretCode, pkcs=8,
                              protection="scryptAndAES128-CBC")

# 將 RSA 金鑰寫入檔案
with open("private.pem", "wb") as f:
    f.write(encryptedKey)

以下是從檔案中將加密保護的 RSA 金鑰讀取出來之後,解密取出金鑰的方法:

from Crypto.PublicKey import RSA

# 保護金鑰的密碼
secretCode = "secret#code"

# 讀取 RSA 金鑰
encodedKey = open("private.pem", "rb").read()

# 解密 RSA 金鑰
key = RSA.import_key(encodedKey, passphrase=secretCode)

RSA 金鑰加密資料

由於 AES 加密演算法的效率遠高於 RSA 加密演算法,所以實務上會先隨機產生一組 AES 金鑰用於加密資料,再以 RSA 金鑰加密 AES 金鑰,最後將加密之後的 AES 金鑰以及密文等資料都儲存下來。

以下是使用 RSA 公鑰搭配 AES 金鑰加密資料的範例:

from Crypto.PublicKey import RSA
from Crypto.Random import get_random_bytes
from Crypto.Cipher import AES, PKCS1_OAEP

# 要加密的資料(必須為 bytes)
data = b'My secret data.'

# 讀取 RSA 公鑰
publicKey = RSA.import_key(open("public.pem").read())

# 建立隨機的 AES Session 金鑰
sessionKey = get_random_bytes(16)

# 以 RSA 金鑰加密 Session 金鑰
cipherRSA = PKCS1_OAEP.new(publicKey)
encSessionKey = cipherRSA.encrypt(sessionKey)

# 以 AES Session 金鑰加密資料
cipherAES = AES.new(sessionKey, AES.MODE_EAX)
ciphertext, tag = cipherAES.encrypt_and_digest(data)

# 將加密結果寫入檔案
with open("encrypted_data.bin", "wb") as f:
    f.write(encSessionKey)
    f.write(cipherAES.nonce)
    f.write(tag)
    f.write(ciphertext)

RSA 金鑰解密資料

以下是用 RSA 私鑰將 AES 金鑰解開之後,再以 AES 金鑰解開密文的範例。

from Crypto.PublicKey import RSA
from Crypto.Cipher import AES, PKCS1_OAEP

# 讀取 RSA 私鑰
privateKey = RSA.import_key(open("private.pem").read())

# 從檔案讀取加密資料
with open("encrypted_data.bin", "rb") as f:
    encSessionKey = f.read(privateKey.size_in_bytes())
    nonce = f.read(16)
    tag = f.read(16)
    ciphertext = f.read(-1)

# 以 RSA 金鑰解密 Session 金鑰
cipherRSA = PKCS1_OAEP.new(privateKey)
sessionKey = cipherRSA.decrypt(encSessionKey)

# 以 AES Session 金鑰解密資料
cipherAES = AES.new(sessionKey, AES.MODE_EAX, nonce)
data = cipherAES.decrypt_and_verify(ciphertext, tag)

# 輸出解密後的資料
print(data.decode("utf-8"))
My secret data.

參考資料:PyCryptodomeNitratine

Share
Published by
Office Guide

Recent Posts

Python 使用 PyAutoGUI 自動操作滑鼠與鍵盤

本篇介紹如何在 Python ...

1 年 ago

Ubuntu Linux 以 WireGuard 架設 VPN 伺服器教學與範例

本篇介紹如何在 Ubuntu ...

1 年 ago

Linux 網路設定 ip 指令用法教學與範例

本篇介紹如何在 Linux 系...

1 年 ago

Linux 以 Cryptsetup、LUKS 加密 USB 隨身碟教學與範例

介紹如何在 Linux 系統中...

1 年 ago