Asymmetric Encryption & Decryption in Python
December 2021 · Derick Zr · 4 minutes read
I needed to send sensitive data between a client and server.
First thought: just send it. Who's going to intercept it?
Terrible idea. Anyone on the network could read it. I needed encryption.
Here's what I learned about cryptography and how to actually implement it.
What Encryption Actually Does
Encryption takes readable data and scrambles it using mathematical algorithms.
Only someone with the right key can unscramble it.
You use it every day: HTTPS, messaging apps, credit card transactions, password storage.
Three Types of Encryption
Symmetric (Secret Key): Same key encrypts and decrypts. Fast. Used for bulk data. Problem: how do you share the key securely?
Asymmetric (Public Key): Two keys. Public key encrypts, private key decrypts. Slower but solves the key-sharing problem. This is what we're implementing.
Hashing: One-way encryption. Can't be decrypted. Used for passwords and integrity checks.
Why Asymmetric Encryption Matters
I can give you my public key openly. You encrypt data with it. Only I can decrypt it with my private key.
No secret key exchange needed.
Let's build this in Python using RSA encryption.
Interactive Demo
Try encryption and decryption right in your browser using the Web Crypto API:
RSA Encryption Playground
React StateGenerate keys, encrypt, and decrypt messages using RSA-OAEP in your browser
Now let's see how to implement the same concepts in Python.
Implementation
Install the Library
Python doesn't have built-in encryption for files. I used the cryptography module.
python -m pip install cryptographyGenerate Keys
RSA needs two keys: private and public.
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import rsa
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
backend=default_backend()
)
public_key = private_key.public_key()Storing Keys
To store the keys in a file, the keys need to be serialized and then written to a file. To store the private key, we need to use the following.
...
pem = private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption()
)
with open('private_key.pem', 'wb') as f:
f.write(pem)
You can protect the contents of this file using a top key serialization password find an example here
Reading Keys
To get the keys out of the files, we need to read each file and then load them. To read the private key, use the following.
...
with open("private_key.pem", "rb") as f:
private_key = serialization.load_pem_private_key(
key_file.read(),
password=None,
backend=default_backend()
)If you store the key with a password, set password to what you used.
The variable private_key will now have the private key. To read the public key, we need to use a slightly modified version.
The variable public_key will now have the public key.
...
with open("public_key.pem", "rb") as f:
public_key = serialization.load_pem_public_key(
key_file.read(),
backend=default_backend()
)EXAMPLE
to show this in action, here is a properly constructed example
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import hashes
# Generating a key
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
backend=default_backend()
)
public_key = private_key.public_key()
# Storing the keys
pem = private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption()
)
with open('Bank/private_key.pem', 'wb') as f:
f.write(pem)
with open('Grace/private_key.pem', 'wb') as f:
f.write(pem)
pem = public_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo
)
with open('public_key.pem', 'wb') as f:
f.write(pem)
f = open('Bank/info.txt', 'rb')
message = f.read()
f.close()
encrypted = public_key.encrypt(
message,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
f = open('Grace/info.encrypted', 'wb')
f.write(encrypted)
f.close()
# Grace RECEIVED THE ENCRYPTED MESSAGE
# AND AUTOMATICALLY THE SYSTEM WILL DECRYPTED IT USING THE PRIVATE KEY of GRACE
original_message = private_key.decrypt(
encrypted,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
with open('Grace/Bank_info.txt','wb') as f:
f.write(original_message)
Encrypting Files
For files, read them in binary mode:
f = open('test.txt', 'rb')
message = f.read()
f.close()Encrypt it, write it to a file, then decrypt when needed.
f = open('test.encrypted', 'wb')
f.write(encrypted)
f.close()The Key Insight
Asymmetric encryption solves the key distribution problem.
You can publish your public key anywhere. People encrypt data with it. Only you can decrypt with your private key.
That's how HTTPS, SSH, and secure messaging work.