88 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			88 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
"""passbook 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.rsa import RSAPrivateKey, RSAPublicKey
 | 
						|
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 passbook.lib.models import CreatedUpdatedModel
 | 
						|
 | 
						|
 | 
						|
class CertificateKeyPair(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[RSAPrivateKey] = None
 | 
						|
    _public_key: Optional[RSAPublicKey] = None
 | 
						|
 | 
						|
    @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[RSAPublicKey]:
 | 
						|
        """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[RSAPrivateKey]:
 | 
						|
        """Get python cryptography PrivateKey instance"""
 | 
						|
        if not self._private_key:
 | 
						|
            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(),
 | 
						|
            )
 | 
						|
        return self._private_key
 | 
						|
 | 
						|
    @property
 | 
						|
    def fingerprint(self) -> str:
 | 
						|
        """Get SHA256 Fingerprint of certificate_data"""
 | 
						|
        return hexlify(self.certificate.fingerprint(hashes.SHA256()), ":").decode(
 | 
						|
            "utf-8"
 | 
						|
        )
 | 
						|
 | 
						|
    @property
 | 
						|
    def kid(self):
 | 
						|
        """Get Key ID used for JWKS"""
 | 
						|
        return "{0}".format(
 | 
						|
            md5(self.key_data.encode("utf-8")).hexdigest()  # nosec
 | 
						|
            if self.key_data
 | 
						|
            else ""
 | 
						|
        )
 | 
						|
 | 
						|
    def __str__(self) -> str:
 | 
						|
        return f"Certificate-Key Pair {self.name} {self.fingerprint}"
 | 
						|
 | 
						|
    class Meta:
 | 
						|
 | 
						|
        verbose_name = _("Certificate-Key Pair")
 | 
						|
        verbose_name_plural = _("Certificate-Key Pairs")
 |