Files
authentik/authentik/crypto/builder.py
dependabot[bot] 93c3b9f70a core: bump cryptography from 42.0.8 to 43.0.0 (#10561)
* core: bump cryptography from 42.0.8 to 43.0.0

Bumps [cryptography](https://github.com/pyca/cryptography) from 42.0.8 to 43.0.0.
- [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pyca/cryptography/compare/42.0.8...43.0.0)

---
updated-dependencies:
- dependency-name: cryptography
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* limit cert common name

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2024-07-22 13:33:19 +02:00

122 lines
4.2 KiB
Python

"""Create self-signed certificates"""
import datetime
import uuid
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import ec, rsa
from cryptography.hazmat.primitives.asymmetric.types import PrivateKeyTypes
from cryptography.x509.oid import NameOID
from django.db import models
from django.utils.translation import gettext_lazy as _
from authentik import __version__
from authentik.crypto.models import CertificateKeyPair
class PrivateKeyAlg(models.TextChoices):
"""Algorithm to create private key with"""
RSA = "rsa", _("rsa")
ECDSA = "ecdsa", _("ecdsa")
class CertificateBuilder:
"""Build self-signed certificates"""
common_name: str
alg: PrivateKeyAlg
def __init__(self, name: str):
self.alg = PrivateKeyAlg.RSA
self.__public_key = None
self.__private_key = None
self.__builder = None
self.__certificate = None
self.common_name = name
self.cert = CertificateKeyPair()
def save(self) -> CertificateKeyPair:
"""Save generated certificate as model"""
if not self.__certificate:
raise ValueError("Certificated hasn't been built yet")
self.cert.name = self.common_name
self.cert.certificate_data = self.certificate
self.cert.key_data = self.private_key
self.cert.save()
return self.cert
def generate_private_key(self) -> PrivateKeyTypes:
"""Generate private key"""
if self.alg == PrivateKeyAlg.ECDSA:
return ec.generate_private_key(curve=ec.SECP256R1())
if self.alg == PrivateKeyAlg.RSA:
return rsa.generate_private_key(
public_exponent=65537, key_size=4096, backend=default_backend()
)
raise ValueError(f"Invalid alg: {self.alg}")
def build(
self,
validity_days: int = 365,
subject_alt_names: list[str] | None = None,
):
"""Build self-signed certificate"""
one_day = datetime.timedelta(1, 0, 0)
self.__private_key = self.generate_private_key()
self.__public_key = self.__private_key.public_key()
alt_names: list[x509.GeneralName] = []
for alt_name in subject_alt_names or []:
if alt_name.strip() != "":
alt_names.append(x509.DNSName(alt_name))
self.__builder = (
x509.CertificateBuilder()
.subject_name(
x509.Name(
[
x509.NameAttribute(NameOID.COMMON_NAME, self.common_name[:64]),
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "authentik"),
x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, "Self-signed"),
]
)
)
.issuer_name(
x509.Name(
[
x509.NameAttribute(NameOID.COMMON_NAME, f"authentik {__version__}"),
]
)
)
.not_valid_before(datetime.datetime.today() - one_day)
.not_valid_after(datetime.datetime.today() + datetime.timedelta(days=validity_days))
.serial_number(int(uuid.uuid4()))
.public_key(self.__public_key)
)
if alt_names:
self.__builder = self.__builder.add_extension(
x509.SubjectAlternativeName(alt_names), critical=True
)
self.__certificate = self.__builder.sign(
private_key=self.__private_key,
algorithm=hashes.SHA256(),
backend=default_backend(),
)
@property
def private_key(self):
"""Return private key in PEM format"""
return self.__private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.TraditionalOpenSSL,
encryption_algorithm=serialization.NoEncryption(),
).decode("utf-8")
@property
def certificate(self):
"""Return certificate in PEM format"""
return self.__certificate.public_bytes(
encoding=serialization.Encoding.PEM,
).decode("utf-8")