This repository has been archived on 2024-05-31. You can view files and clone it, but cannot push or open issues or pull requests.
authentik/authentik/crypto/models.py
dependabot[bot] 18cfe67719
core: bump black from 22.12.0 to 23.1.0 (#4584)
* core: bump black from 22.12.0 to 23.1.0

Bumps [black](https://github.com/psf/black) from 22.12.0 to 23.1.0.
- [Release notes](https://github.com/psf/black/releases)
- [Changelog](https://github.com/psf/black/blob/main/CHANGES.md)
- [Commits](https://github.com/psf/black/compare/22.12.0...23.1.0)

---
updated-dependencies:
- dependency-name: black
  dependency-type: direct:development
  update-type: version-update:semver-major
...

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

* re-format

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

* fix

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>
2023-02-01 11:31:32 +01:00

103 lines
3.6 KiB
Python

"""authentik crypto models"""
from binascii import hexlify
from hashlib import md5
from typing import Optional
from uuid import uuid4
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric.types import PRIVATE_KEY_TYPES, PUBLIC_KEY_TYPES
from cryptography.hazmat.primitives.serialization import load_pem_private_key
from cryptography.x509 import Certificate, load_pem_x509_certificate
from django.db import models
from django.utils.translation import gettext_lazy as _
from rest_framework.serializers import Serializer
from structlog.stdlib import get_logger
from authentik.blueprints.models import ManagedModel
from authentik.lib.models import CreatedUpdatedModel, SerializerModel
LOGGER = get_logger()
class CertificateKeyPair(SerializerModel, ManagedModel, CreatedUpdatedModel):
"""CertificateKeyPair that can be used for signing or encrypting if `key_data`
is set, otherwise it can be used to verify remote data."""
kp_uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4)
name = models.TextField()
certificate_data = models.TextField(help_text=_("PEM-encoded Certificate data"))
key_data = models.TextField(
help_text=_(
"Optional Private Key. If this is set, you can use this keypair for encryption."
),
blank=True,
default="",
)
_cert: Optional[Certificate] = None
_private_key: Optional[PRIVATE_KEY_TYPES] = None
_public_key: Optional[PUBLIC_KEY_TYPES] = None
@property
def serializer(self) -> Serializer:
from authentik.crypto.api import CertificateKeyPairSerializer
return CertificateKeyPairSerializer
@property
def certificate(self) -> Certificate:
"""Get python cryptography Certificate instance"""
if not self._cert:
self._cert = load_pem_x509_certificate(
self.certificate_data.encode("utf-8"), default_backend()
)
return self._cert
@property
def public_key(self) -> Optional[PUBLIC_KEY_TYPES]:
"""Get public key of the private key"""
if not self._public_key:
self._public_key = self.private_key.public_key()
return self._public_key
@property
def private_key(
self,
) -> Optional[PRIVATE_KEY_TYPES]:
"""Get python cryptography PrivateKey instance"""
if not self._private_key and self.key_data != "":
try:
self._private_key = load_pem_private_key(
str.encode("\n".join([x.strip() for x in self.key_data.split("\n")])),
password=None,
backend=default_backend(),
)
except ValueError as exc:
LOGGER.warning(exc)
return None
return self._private_key
@property
def fingerprint_sha256(self) -> str:
"""Get SHA256 Fingerprint of certificate_data"""
return hexlify(self.certificate.fingerprint(hashes.SHA256()), ":").decode("utf-8")
@property
def fingerprint_sha1(self) -> str:
"""Get SHA1 Fingerprint of certificate_data"""
return hexlify(self.certificate.fingerprint(hashes.SHA1()), ":").decode("utf-8") # nosec
@property
def kid(self):
"""Get Key ID used for JWKS"""
return md5(self.key_data.encode("utf-8")).hexdigest() if self.key_data else "" # nosec
def __str__(self) -> str:
return f"Certificate-Key Pair {self.name}"
class Meta:
verbose_name = _("Certificate-Key Pair")
verbose_name_plural = _("Certificate-Key Pairs")