Python cryptography cheatsheet¶
simple https server¶
# python2
>>> import BaseHTTPServer, SimpleHTTPServer
>>> import ssl
>>> host, port = 'localhost', 5566
>>> handler = SimpleHTTPServer.SimpleHTTPRequestHandler
>>> httpd = BaseHTTPServer.HTTPServer((host, port), handler)
>>> httpd.socket = ssl.wrap_socket(httpd.socket,
... certfile='./cert.crt',
... keyfile='./cert.key',
... server_side=True)
>>> httpd.serve_forever()
# python3
>>> from http import server
>>> handler = server.SimpleHTTPRequestHandler
>>> import ssl
>>> host, port = 'localhost', 5566
>>> httpd = server.HTTPServer((host, port), handler)
>>> httpd.socket = ssl.wrap_socket(httpd.socket,
... certfile='./cert.crt',
... keyfile='./cert.key',
... server_side=True)
...
>>> httpd.serve_forever()
check certificate information¶
from cryptography import x509
from cryptography.hazmat.backends import default_backend
backend = default_backend()
with open('./cert.crt', 'rb') as f:
crt_data = f.read()
cert = x509.load_pem_x509_certificate(crt_data, backend)
class Certificate:
_fields = ['country_name',
'state_or_province_name',
'locality_name',
'organization_name',
'organizational_unit_name',
'common_name',
'email_address']
def __init__(self, cert):
assert isinstance(cert, x509.Certificate)
self._cert = cert
for attr in self._fields:
oid = getattr(x509, 'OID_' + attr.upper())
subject = cert.subject
info = subject.get_attributes_for_oid(oid)
setattr(self, attr, info)
cert = Certificate(cert)
for attr in cert._fields:
for info in getattr(cert, attr):
print("{}: {}".format(info._oid._name, info._value))
output:
$ genrsa -out cert.key
Generating RSA private key, 1024 bit long modulus
..........++++++
...++++++
e is 65537 (0x10001)
$ openssl req -x509 -new -nodes \
> -key cert.key -days 365 \
> -out cert.crt
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:TW
State or Province Name (full name) [Some-State]:Taiwan
Locality Name (eg, city) []:Taipei
Organization Name (eg, company) [Internet Widgits Pty Ltd]:personal
Organizational Unit Name (eg, section) []:perfonal
Common Name (e.g. server FQDN or YOUR name) []:localhost
Email Address []:test@example.com
$ python3 cert.py
countryName: TW
stateOrProvinceName: Taiwan
localityName: Taipei
organizationName: personal
organizationalUnitName: perfonal
commonName: localhost
emailAddress: test@example.com
generate RSA keyfile without passphrase¶
# $ openssl genrsa cert.key 2048
>>> from cryptography.hazmat.backends import default_backend
>>> from cryptography.hazmat.primitives import serialization
>>> from cryptography.hazmat.primitives.asymmetric import rsa
>>> key = rsa.generate_private_key(
... public_exponent=65537,
... key_size=2048,
... backend=default_backend())
...
>>> with open('cert.key', 'wb') as f:
... f.write(key.private_bytes(
... encoding=serialization.Encoding.PEM,
... format=serialization.PrivateFormat.TraditionalOpenSSL,
... encryption_algorithm=serialization.NoEncryption()))
HMAC - check integrity of a message¶
>>> import socket
>>> import hmac
>>> import hashlib
>>> secret_key = b"Alice & Bob secret key"
>>> def verify(digest, msg):
... h = hmac.new(secret_key, msg, hashlib.sha256)
... if h.digest() != digest:
... raise ValueError("Check integrity fail")
...
>>> alice_msg = b"Hello Bob"
>>> h = hmac.new(secret_key, alice_msg, hashlib.sha256)
>>> alice_digest = h.digest()
>>> alice, bob = socket.socketpair()
>>> _ = alice.send(alice_msg) # Alice send msg to Bob
>>> msg = bob.recv(1024) # Bob recv msg from Alice
>>> _ = alice.send(alice_digest) # Alice send digest to Bob
>>> digest = bob.recv(1024) # Bob recv digest from Alice
>>> verify(digest, msg) # Bob check msg integrity
>>> # if message be modified by someone, check integrity fail
>>> verify(digest, b"Hello Attack")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in verify
ValueError: Check integrity fail
Check integrity fail
Using DSA to proof of identity¶
import socket
from cryptography.exceptions import InvalidSignature
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import dsa
alice, bob = socket.socketpair()
def gen_dsa_key():
private_key = dsa.generate_private_key(
key_size=2048, backend=default_backend())
return private_key, private_key.public_key()
def sign_data(data, private_key):
signature = private_key.sign(data, hashes.SHA256())
return signature
def verify_data(data, signature, public_key):
try:
public_key.verify(signature, data, hashes.SHA256())
except InvalidSignature:
print("recv msg: {} not trust!".format(data))
else:
print("check msg: {} success!".format(data))
# generate alice private & public key
alice_private_key, alice_public_key = gen_dsa_key()
# alice send message to bob, then bob recv
alice_msg = b"Hello Bob"
b = alice.send(alice_msg)
bob_recv_msg = bob.recv(1024)
# alice send signature to bob, then bob recv
signature = sign_data(alice_msg, alice_private_key)
b = alice.send(signature)
bob_recv_signature = bob.recv(1024)
# bob check message recv from alice
verify_data(bob_recv_msg, bob_recv_signature, alice_public_key)
# attacker modify the msg will make the msg check fail
verify_data(b"I'm attacker!", bob_recv_signature, alice_public_key)
output:
$ python3 test_dsa.py
check msg: b'Hello Bob' success!
recv msg: b"I'm attacker!" not trust!
Simple Diffie-Hellman key exchange¶
"""
p (public know)
n (public know)
Alice Bob
alice_y = 23 bob_y = 16
alice_x = (n ** alice_y) % p --->
<--- bob_x = (n ** bob_y) % p
k = (bob_x ** alice_y) %p k = (alice_x ** bob_y) % p
"""
>>> import socket
>>> alice, bob = socket.socketpair()
>>> p = 353 # public know
>>> n = 3 # public know
>>> alice_y = 23 # only alice know
>>> bob_y = 16 # only bob know
>>> alice_x = (n ** alice_y) % p
>>> num_bytes = alice.send(alice_x.to_bytes(4, 'big'))
>>> msg = bob.recv(1024)
>>> bob_recv_x = int.from_bytes(msg, 'big')
>>> bob_x = (n ** bob_y) % p
>>> num_bytes = bob.send(bob_x.to_bytes(4, 'big'))
>>> msg = alice.recv(1024)
>>> alice_recv_x = int.from_bytes(msg, 'big')
>>> alice_key = (alice_recv_x ** alice_y) % p
>>> bob_key = (bob_recv_x ** bob_y) % p
>>> alice_key
136
>>> bob_key
136
AES CBC mode encrypt and decrypt¶
import os
from cryptography.hazmat.primitives import padding
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.ciphers import (
Cipher,
algorithms,
modes)
backend = default_backend()
key = os.urandom(32)
iv = os.urandom(16)
cipher = Cipher(algorithms.AES(key),
modes.CBC(iv),
backend=backend)
# PS. CBC mode need padding
def encrypt(p_text, cipher):
# add padding to plain text
padder = padding.PKCS7(128).padder() # 128 bit
text = padder.update(p_text) + padder.finalize()
# encrypt plain text
encryptor = cipher.encryptor()
c_text = encryptor.update(text) + encryptor.finalize()
return c_text
def decrypt(c_text, cipher):
# decrypt plain text with padding
decryptor = cipher.decryptor()
text = decryptor.update(c_text) + decryptor.finalize()
# remove padding
unpadder = padding.PKCS7(128).unpadder() # 128 bit
p_text = unpadder.update(text) + unpadder.finalize()
return p_text
text = b"Hello Encrypt"
c_text = encrypt(text, cipher)
print(c_text)
p_text = decrypt(c_text, cipher)
print(p_text)
output:
$ python3 aes_cbc.py
b',n\xcb\xd3\x95\xfayvX\xa6q\\\x19\xdb\x12C'
b'Hello Encrypt'
AES CTR mode encrypt and decrypt¶
>>> import os
>>> from cryptography.hazmat.backends import default_backend
>>> from cryptography.hazmat.primitives.ciphers import (
... Cipher,
... algorithms,
... modes)
>>> backend = default_backend()
>>> key = os.urandom(32)
>>> nonce = os.urandom(16)
>>> # CTR mode does not require padding
>>> cipher = Cipher(algorithms.AES(key),
... modes.CTR(nonce),
... backend=backend)
>>> encryptor = cipher.encryptor()
>>> p_text = b"Hello Encrypt"
>>> ct = encryptor.update(p_text) + encryptor.finalize()
>>> ct
b'o\xb3;\x079\xde\x86@\xec^o\x1f\x9f'
>>> decryptor = cipher.decryptor()
>>> pt = decryptor.update(ct) + decryptor.finalize()
>>> pt
b'Hello Encrypt'
Require padding or not¶
mode | require padding |
---|---|
CBC(initialization_vector) | YES |
CTR(nonce) | NO |
OFB(initialization_vector) | NO |
CFB(initialization_vector) | NO |
CFB8(initialization_vector) | NO |
GCM(initialization_vector) | NO |