Skip to content

Instantly share code, notes, and snippets.

@badri
Last active October 5, 2022 08:24
Show Gist options
  • Save badri/607a6071594512ccfd62b4bea31ea284 to your computer and use it in GitHub Desktop.
Save badri/607a6071594512ccfd62b4bea31ea284 to your computer and use it in GitHub Desktop.
import jwt
from datetime import datetime, timedelta
import ipaddress
import time
from cryptography.hazmat.primitives import serialization
from cryptography.x509 import load_pem_x509_certificate
def generate_selfsigned_cert(hostname, ip_addresses=None, key=None):
"""Generates self signed certificate for a hostname, and optional IP addresses."""
from cryptography import x509
from cryptography.x509.oid import NameOID
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
# Generate our key
if key is None:
key = rsa.generate_private_key(
public_exponent=65537,
key_size=768,
backend=default_backend(),
)
name = x509.Name([
x509.NameAttribute(NameOID.COMMON_NAME, hostname)
])
# best practice seem to be to include the hostname in the SAN, which *SHOULD* mean COMMON_NAME is ignored.
alt_names = [x509.DNSName(hostname)]
# allow addressing by IP, for when you don't have real DNS (common in most testing scenarios
if ip_addresses:
for addr in ip_addresses:
# openssl wants DNSnames for ips...
alt_names.append(x509.DNSName(addr))
# ... whereas golang's crypto/tls is stricter, and needs IPAddresses
# note: older versions of cryptography do not understand ip_address objects
alt_names.append(x509.IPAddress(ipaddress.ip_address(addr)))
san = x509.SubjectAlternativeName(alt_names)
# path_len=0 means this cert can only sign itself, not other certs.
basic_contraints = x509.BasicConstraints(ca=True, path_length=0)
now = datetime.utcnow()
cert = (
x509.CertificateBuilder()
.subject_name(name)
.issuer_name(name)
.public_key(key.public_key())
.serial_number(1000)
.not_valid_before(now)
.not_valid_after(now + timedelta(days=10*365))
.add_extension(basic_contraints, False)
.add_extension(san, False)
.sign(key, hashes.SHA256(), default_backend())
)
cert_pem = cert.public_bytes(encoding=serialization.Encoding.PEM)
key_pem = key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.TraditionalOpenSSL,
encryption_algorithm=serialization.NoEncryption(),
)
return cert_pem, key_pem
# RS384, RS512
def generate_token(private_key, expiry_hours, kid, typ="JWT", alg="RS256"):
future = datetime.now() + timedelta(hours=expiry_hours)
exp = int(time.mktime(future.timetuple()))
return jwt.encode({"some": "payload", "exp": exp}, private_key, algorithm=alg, headers={"kid": kid, "typ": typ, "alg": alg,},)
def get_public_key(cert_str):
cert_obj = load_pem_x509_certificate(cert_str)
public_key = cert_obj.public_key()
pem = public_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo,
)
return pem.decode('utf-8')
"""
cert, private_key = generate_selfsigned_cert('example.com')
print(cert)
print(private_key)
token = generate_token(private_key, 1, "17GLrwRZj5LzsI7lNyxTJd0EGPeXYT6qbb7lcLsCTwQ", "JWS")
print(token)
public_key = get_public_key(cert)
print(public_key)
"""
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment