介紹如何在 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.
參考資料:PyCryptodome、Nitratine