| 1 | """Utilities for generating TLS certificates.""" |
|---|
| 2 | |
|---|
| 3 | import datetime |
|---|
| 4 | |
|---|
| 5 | from cryptography import x509 |
|---|
| 6 | from cryptography.x509.oid import NameOID |
|---|
| 7 | from cryptography.hazmat.primitives.asymmetric import rsa |
|---|
| 8 | from cryptography.hazmat.primitives import serialization, hashes |
|---|
| 9 | |
|---|
| 10 | from twisted.python.filepath import FilePath |
|---|
| 11 | |
|---|
| 12 | |
|---|
| 13 | def cert_to_file(path: FilePath, cert) -> FilePath: |
|---|
| 14 | """ |
|---|
| 15 | Write the given certificate to a file on disk. Returns the path. |
|---|
| 16 | """ |
|---|
| 17 | path.setContent(cert.public_bytes(serialization.Encoding.PEM)) |
|---|
| 18 | return path |
|---|
| 19 | |
|---|
| 20 | |
|---|
| 21 | def private_key_to_file(path: FilePath, private_key) -> FilePath: |
|---|
| 22 | """ |
|---|
| 23 | Write the given key to a file on disk. Returns the path. |
|---|
| 24 | """ |
|---|
| 25 | path.setContent( |
|---|
| 26 | private_key.private_bytes( |
|---|
| 27 | encoding=serialization.Encoding.PEM, |
|---|
| 28 | format=serialization.PrivateFormat.TraditionalOpenSSL, |
|---|
| 29 | encryption_algorithm=serialization.NoEncryption(), |
|---|
| 30 | ) |
|---|
| 31 | ) |
|---|
| 32 | return path |
|---|
| 33 | |
|---|
| 34 | |
|---|
| 35 | def generate_private_key(): |
|---|
| 36 | """Create a RSA private key.""" |
|---|
| 37 | return rsa.generate_private_key(public_exponent=65537, key_size=2048) |
|---|
| 38 | |
|---|
| 39 | |
|---|
| 40 | def generate_certificate( |
|---|
| 41 | private_key, |
|---|
| 42 | expires_days: int = 10, |
|---|
| 43 | valid_in_days: int = 0, |
|---|
| 44 | org_name: str = "Yoyodyne", |
|---|
| 45 | ): |
|---|
| 46 | """Generate a certificate from a RSA private key.""" |
|---|
| 47 | subject = issuer = x509.Name( |
|---|
| 48 | [x509.NameAttribute(NameOID.ORGANIZATION_NAME, org_name)] |
|---|
| 49 | ) |
|---|
| 50 | starts = datetime.datetime.utcnow() + datetime.timedelta(days=valid_in_days) |
|---|
| 51 | expires = datetime.datetime.utcnow() + datetime.timedelta(days=expires_days) |
|---|
| 52 | return ( |
|---|
| 53 | x509.CertificateBuilder() |
|---|
| 54 | .subject_name(subject) |
|---|
| 55 | .issuer_name(issuer) |
|---|
| 56 | .public_key(private_key.public_key()) |
|---|
| 57 | .serial_number(x509.random_serial_number()) |
|---|
| 58 | .not_valid_before(min(starts, expires)) |
|---|
| 59 | .not_valid_after(expires) |
|---|
| 60 | .add_extension( |
|---|
| 61 | x509.SubjectAlternativeName([x509.DNSName("localhost")]), |
|---|
| 62 | critical=False, |
|---|
| 63 | # Sign our certificate with our private key |
|---|
| 64 | ) |
|---|
| 65 | .sign(private_key, hashes.SHA256()) |
|---|
| 66 | ) |
|---|