Source code for request.operation_processor.csr_sign

"""CSR operation processor classes."""
from abc import abstractmethod
from typing import get_args

from cryptography import x509
from cryptography.hazmat.primitives import hashes
from trustpoint_core.crypto_types import AllowedCertSignHashAlgos
from trustpoint_core.oid import SignatureSuite

from pki.models import CredentialModel
from request.request_context import BaseCertificateRequestContext, BaseRequestContext
from trustpoint.logger import LoggerMixin

from .base import AbstractOperationProcessor


[docs] class EstCsrSignProcessor(LoggerMixin, AbstractOperationProcessor): """Base operation processor for signing a CSR (Certificate Signing Request) for EST protocol.""" _signed_csr: x509.CertificateSigningRequest | None = None @abstractmethod def _get_signing_credential(self, context: BaseCertificateRequestContext) -> CredentialModel: """Get the credential to use for signing the CSR. Args: context: Request context containing necessary credentials. Returns: The credential to use for signing. Raises: ValueError: If the required credential is not available. """
[docs] def process_operation(self, context: BaseRequestContext) -> None: """Sign the CSR from cert_requested using the appropriate credential. Args: context: Request context containing the CSR in cert_requested and the signing credential. Raises: TypeError: If context is not a BaseCertificateRequestContext. ValueError: If required context attributes are missing. """ if not isinstance(context, BaseCertificateRequestContext): exc_msg = 'CSR signing requires a BaseCertificateRequestContext.' raise TypeError(exc_msg) if context.cert_requested is None: exc_msg = 'CSR (cert_requested) must be set in the context.' raise ValueError(exc_msg) if not isinstance(context.cert_requested, x509.CertificateSigningRequest): exc_msg = ( f'cert_requested must be a CertificateSigningRequest, ' f'but found {type(context.cert_requested)}.' ) raise TypeError(exc_msg) signing_credential = self._get_signing_credential(context) csr = context.cert_requested # Determine hash algorithm if signing_credential.certificate: signature_suite = SignatureSuite.from_certificate(signing_credential.get_certificate()) hash_algorithm_enum = signature_suite.algorithm_identifier.hash_algorithm if hash_algorithm_enum is None: err_msg = 'Failed to get hash algorithm from signing certificate.' raise ValueError(err_msg) hash_algorithm = hash_algorithm_enum.hash_algorithm() else: hash_algorithm = hashes.SHA256() if not isinstance(hash_algorithm, get_args(AllowedCertSignHashAlgos)): err_msg = ( f'The hash algorithm must be one of {AllowedCertSignHashAlgos}, ' f'but found {type(hash_algorithm)}' ) raise TypeError(err_msg) csr_builder = x509.CertificateSigningRequestBuilder() csr_builder = csr_builder.subject_name(csr.subject) for extension in csr.extensions: csr_builder = csr_builder.add_extension(extension.value, extension.critical) private_key = signing_credential.get_private_key_serializer().as_crypto() self._signed_csr = csr_builder.sign(private_key=private_key, algorithm=hash_algorithm) if signing_credential.certificate: self.logger.info( 'Signed CSR for EST with credential: %s', signing_credential.get_certificate().subject.rfc4514_string() ) else: self.logger.info('Signed CSR for EST with credential (certificate pending)')
[docs] def get_signed_csr(self) -> x509.CertificateSigningRequest: """Get the signed CSR. Returns: The signed CertificateSigningRequest. Raises: ValueError: If CSR has not been signed yet. """ if self._signed_csr is None: exc_msg = 'CSR not signed. Call process_operation first.' raise ValueError(exc_msg) return self._signed_csr
[docs] class EstCaCsrSignProcessor(EstCsrSignProcessor): """Operation processor for signing a CSR with the CA's issuer credential for EST protocol.""" def _get_signing_credential(self, context: BaseCertificateRequestContext) -> CredentialModel: """Get the issuer credential (CA) for signing the CSR. Args: context: Request context containing the issuer credential or domain. Returns: The issuer credential to use for signing. Raises: ValueError: If issuer credential is not available. """ if not context.issuer_credential: if not context.domain: exc_msg = 'Domain must be set in the context to get issuer credential for CSR signing.' raise ValueError(exc_msg) ca = context.domain.get_issuing_ca_or_value_error() context.issuer_credential = ca.get_credential() if not context.issuer_credential: exc_msg = 'Issuing CA does not have a credential' raise ValueError(exc_msg) return context.issuer_credential
[docs] class EstDeviceCsrSignProcessor(EstCsrSignProcessor): """Operation processor for signing a CSR with the device's owner credential for EST protocol.""" def _get_signing_credential(self, context: BaseCertificateRequestContext) -> CredentialModel: """Get the owner credential (device) for signing the CSR. Args: context: Request context containing the owner credential. Returns: The owner credential to use for signing. Raises: ValueError: If owner credential is not available. """ if not context.owner_credential: exc_msg = 'Owner credential must be set in the context for device CSR signing.' raise ValueError(exc_msg) return context.owner_credential