Unlock RSA Encryption and Digital Signatures in Python – Complete Guide
This article explains RSA asymmetric encryption, key pair generation, encryption/decryption limits, digital signing and verification, and provides a full Python implementation using PyCryptodome, complete with code snippets, base64 handling, and practical usage tips.
Introduction
Encryption transforms plaintext into ciphertext using a key; decryption reverses the process. In many internet scenarios data is transmitted without encryption, exposing it to theft. RSA provides asymmetric encryption where a public key encrypts data and a private key decrypts it, enabling secure transmission and authentication.
RSA Algorithm Overview
Symmetric encryption uses the same key for both operations, while asymmetric encryption (RSA) uses a public key for encryption and a private key for decryption. A sender S obtains the receiver R’s public key, encrypts the message, and R decrypts it with its private key. Because only the private key can decrypt, eavesdroppers cannot read the data.
In a man‑in‑the‑middle scenario, an attacker could replace R’s public key, so digital signatures are required to verify the sender’s identity. The signing process hashes the message (with a salt), encrypts the hash with the sender’s private key, and the receiver verifies the signature using the sender’s public key.
Key Points for Implementing RSA in Python
1. Install the cryptographic library: pip install pycryptodome.
2. Convert between str and bytes using encode() and decode(). RSA operations require bytes data.
3. Generate a key pair (recommended 2048 bits). The maximum encryptable block size is key_length/8 − 11 bytes (245 bytes for a 2048‑bit key).
from Crypto import Random
from Crypto.PublicKey import RSA
# Pseudo‑random generator
random_gen = Random.new().read
# Generate RSA key pair (2048 bits)
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)4. Base64 encoding is used to transmit binary data as ASCII. The urlsafe_b64encode and urlsafe_b64decode helpers replace ‘+’/‘/’ with ‘‑’/‘_’ for URL safety.
Python RSA Encryption, Decryption, Signing and Verification Class
The following RSACipher class wraps the full workflow, supporting chunked encryption of long strings (including Chinese characters), decryption, signing, and verification.
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 encryption, decryption, signing, and verification utilities."""
def read_xml(self, xmlfile):
"""Read plaintext from an XML file."""
with open(xmlfile, 'r', encoding="utf-8") as file:
xmlstr = file.read()
print(xmlstr)
return xmlstr
def encrypt_file(self, encrypt_file):
"""Read encrypted binary data from a file."""
with open(encrypt_file, 'rb') as f:
message = f.read()
return message
def Encrypt(self, message, publicKeyfile, out_file):
"""Encrypt a plaintext string using a public key.
Returns base64‑encoded ciphertext.
"""
with open(publicKeyfile, 'r') as f:
publicKey = f.read()
pubKey = RSA.importKey(publicKey)
cipher = Cipher_PKCS1_v1_5.new(pubKey)
message = message.encode()
length = len(message)
default_length = 245 # max block size for 2048‑bit key
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(encrypt_text)
return encrypt_text
def Decrypt(self, message, privateKeyfile, out_file):
"""Decrypt base64‑encoded ciphertext using a private key.
Returns the original plaintext string.
"""
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 = 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):
"""Create a digital signature for a message using a private key.
Returns a base64‑encoded signature string.
"""
with open(private_sign_file, 'r') as f:
private_sign = f.read()
message = message.encode()
private_key = RSA.importKey(private_sign)
hash_value = SHA.new(message)
signer = Sig_pk.new(private_key)
signature = signer.sign(hash_value)
result = base64.b64encode(signature).decode()
return result
def verify(self, message, public_sign_file, signature):
"""Verify a base64‑encoded signature using the sender's public key.
Returns True if the signature is valid.
"""
with open(public_sign_file, 'r') as f:
public_sign = f.read()
signature = base64.b64decode(signature)
public_key = RSA.importKey(public_sign)
hash_value = SHA.new(message.encode())
verifier = Sig_pk.new(public_key)
return verifier.verify(hash_value, signature)
if __name__ == '__main__':
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('Encrypted:
%s' % encrypt_text)
private_sign_file = "private.pem"
signature = rsacipher.sign(message, private_sign_file)
print('Signature:
%s' % signature)
decryptFile = "deencrypt.txt"
privateFile = "rsa.key"
decrypt_text = rsacipher.Decrypt(encrypt_text, privateFile, decryptFile)
print('Decrypted:
%s' % decrypt_text)
public_sign_file = "public.pem"
result = rsacipher.verify(decrypt_text, public_sign_file, signature)
print('Verification:
%s' % result)The class handles chunked encryption/decryption to accommodate data larger than the RSA block size, automatically encodes/decodes with Base64, and ensures that the original message string is preserved after signing and verification.
Supporting Diagrams
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Python Crawling & Data Mining
Life's short, I code in Python. This channel shares Python web crawling, data mining, analysis, processing, visualization, automated testing, DevOps, big data, AI, cloud computing, machine learning tools, resources, news, technical articles, tutorial videos and learning materials. Join us!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
