Source code for django_ca.key_backends.hsm.ocsp_backend

# This file is part of django-ca (https://github.com/mathiasertl/django-ca).
#
# django-ca is free software: you can redistribute it and/or modify it under the terms of the GNU General
# Public License as published by the Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# django-ca is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along with django-ca. If not, see
# <http://www.gnu.org/licenses/>.

"""OCSP key backend using HSMs."""

from collections.abc import Iterator
from contextlib import contextmanager
from typing import TYPE_CHECKING

from pkcs11 import Session

from cryptography import x509
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.x509.ocsp import OCSPResponse, OCSPResponseBuilder

from django_ca.constants import ELLIPTIC_CURVE_NAMES
from django_ca.key_backends import OCSPKeyBackend
from django_ca.key_backends.hsm.keys import (
    PKCS11Ed448PrivateKey,
    PKCS11Ed25519PrivateKey,
    PKCS11EllipticCurvePrivateKey,
    PKCS11PrivateKeyTypes,
    PKCS11RSAPrivateKey,
)
from django_ca.key_backends.hsm.mixins import HSMKeyBackendMixin
from django_ca.typehints import ParsableKeyType, SignatureHashAlgorithm
from django_ca.utils import int_to_hex

if TYPE_CHECKING:
    from django_ca.models import CertificateAuthority


[docs] class HSMOCSPBackend(HSMKeyBackendMixin, OCSPKeyBackend): """OCSP key backend storing files on the local file system.""" # Backend options storage_alias: str path: str encrypt_private_key: bool @contextmanager def session(self, rw: bool = False) -> Iterator[Session]: # type: ignore[override] with super().session(so_pin=self.so_pin, user_pin=self.user_pin, rw=rw) as session: yield session def create_private_key( self, ca: "CertificateAuthority", key_type: ParsableKeyType, key_size: int | None, elliptic_curve: ec.EllipticCurve | None, ) -> x509.CertificateSigningRequest: key_id = int_to_hex(x509.random_serial_number()) key_label = f"ocsp_{key_id}" elliptic_curve_name = None if elliptic_curve is not None: elliptic_curve_name = ELLIPTIC_CURVE_NAMES[type(elliptic_curve)] with self.session(rw=True) as session: # Generate the private key. private_key = self._create_private_key( session, key_id, key_label, key_type, key_size=key_size, elliptic_curve=elliptic_curve_name, ) # Generate the CSR to return to the caller. csr_builder = x509.CertificateSigningRequestBuilder().subject_name(x509.Name([])) csr_algorithm = self.get_csr_algorithm(key_type) csr = csr_builder.sign(private_key, csr_algorithm) ca.ocsp_key_backend_options["private_key"]["key_id"] = key_id ca.ocsp_key_backend_options["private_key"]["key_label"] = key_label ca.ocsp_key_backend_options["private_key"]["key_type"] = key_type return csr def sign_ocsp_response( self, ca: "CertificateAuthority", builder: OCSPResponseBuilder, signature_hash_algorithm: SignatureHashAlgorithm | None, ) -> OCSPResponse: key_id = ca.ocsp_key_backend_options["private_key"]["key_id"] key_label = ca.ocsp_key_backend_options["private_key"]["key_label"] key_type = ca.ocsp_key_backend_options["private_key"]["key_type"] with self.session() as session: if key_type == "RSA": private_key: PKCS11PrivateKeyTypes = PKCS11RSAPrivateKey(session, key_id, key_label) elif key_type == "Ed448": private_key = PKCS11Ed448PrivateKey(session, key_id, key_label) elif key_type == "Ed25519": private_key = PKCS11Ed25519PrivateKey(session, key_id, key_label) elif key_type == "EC": private_key = PKCS11EllipticCurvePrivateKey(session, key_id, key_label) else: # pragma: no cover raise ValueError(f"{key_type}: Unsupported key type.") return builder.sign(private_key, signature_hash_algorithm)