File size: 2,866 Bytes
ecf9cb9 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | """
crypto/keys.py
RSA key lifecycle: generate, self-sign X.509, serialise/load PEM.
Pure stdlib + cryptography library. No lxml, no xmlsec.
"""
import datetime
from dataclasses import dataclass
from cryptography import x509
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.x509.oid import NameOID
@dataclass
class KeyPair:
private_key: rsa.RSAPrivateKey
public_key: rsa.RSAPublicKey
certificate: x509.Certificate
entity_id: str
def generate_key_pair(entity_id: str, key_size: int = 2048) -> KeyPair:
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=key_size,
)
public_key = private_key.public_key()
subject = issuer = x509.Name([
x509.NameAttribute(NameOID.COMMON_NAME, entity_id),
])
now = datetime.datetime.utcnow()
cert = (
x509.CertificateBuilder()
.subject_name(subject)
.issuer_name(issuer)
.public_key(public_key)
.serial_number(x509.random_serial_number())
.not_valid_before(now)
.not_valid_after(now + datetime.timedelta(days=3650))
.add_extension(
x509.KeyUsage(
digital_signature=True,
content_commitment=False,
key_encipherment=True,
data_encipherment=False,
key_agreement=False,
key_cert_sign=False,
crl_sign=False,
encipher_only=False,
decipher_only=False,
),
critical=True,
)
.sign(private_key, hashes.SHA256())
)
return KeyPair(
private_key=private_key,
public_key=public_key,
certificate=cert,
entity_id=entity_id,
)
def save_pem(key_pair: KeyPair, private_path: str, cert_path: str) -> None:
with open(private_path, 'wb') as f:
f.write(key_pair.private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption(),
))
with open(cert_path, 'wb') as f:
f.write(key_pair.certificate.public_bytes(serialization.Encoding.PEM))
def load_private_key(path: str) -> rsa.RSAPrivateKey:
with open(path, 'rb') as f:
return serialization.load_pem_private_key(f.read(), password=None)
def load_certificate(path: str) -> x509.Certificate:
with open(path, 'rb') as f:
return x509.load_pem_x509_certificate(f.read())
def cert_to_b64(cert: x509.Certificate) -> str:
import base64
der = cert.public_bytes(serialization.Encoding.DER)
return base64.b64encode(der).decode('ascii')
|