123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180 |
- # This file is dual licensed under the terms of the Apache License, Version
- # 2.0, and the BSD License. See the LICENSE file in the root of this repository
- # for complete details.
- import typing
- from cryptography import utils
- from cryptography import x509
- from cryptography.hazmat.primitives import hashes, serialization
- from cryptography.hazmat.primitives.asymmetric import ec, rsa
- from cryptography.utils import _check_byteslike
- def load_pem_pkcs7_certificates(data: bytes) -> typing.List[x509.Certificate]:
- from cryptography.hazmat.backends.openssl.backend import backend
- return backend.load_pem_pkcs7_certificates(data)
- def load_der_pkcs7_certificates(data: bytes) -> typing.List[x509.Certificate]:
- from cryptography.hazmat.backends.openssl.backend import backend
- return backend.load_der_pkcs7_certificates(data)
- def serialize_certificates(
- certs: typing.List[x509.Certificate],
- encoding: serialization.Encoding,
- ) -> bytes:
- from cryptography.hazmat.backends.openssl.backend import backend
- return backend.pkcs7_serialize_certificates(certs, encoding)
- _ALLOWED_PKCS7_HASH_TYPES = typing.Union[
- hashes.SHA1,
- hashes.SHA224,
- hashes.SHA256,
- hashes.SHA384,
- hashes.SHA512,
- ]
- _ALLOWED_PRIVATE_KEY_TYPES = typing.Union[
- rsa.RSAPrivateKey, ec.EllipticCurvePrivateKey
- ]
- class PKCS7Options(utils.Enum):
- Text = "Add text/plain MIME type"
- Binary = "Don't translate input data into canonical MIME format"
- DetachedSignature = "Don't embed data in the PKCS7 structure"
- NoCapabilities = "Don't embed SMIME capabilities"
- NoAttributes = "Don't embed authenticatedAttributes"
- NoCerts = "Don't embed signer certificate"
- class PKCS7SignatureBuilder:
- def __init__(
- self,
- data: typing.Optional[bytes] = None,
- signers: typing.List[
- typing.Tuple[
- x509.Certificate,
- _ALLOWED_PRIVATE_KEY_TYPES,
- _ALLOWED_PKCS7_HASH_TYPES,
- ]
- ] = [],
- additional_certs: typing.List[x509.Certificate] = [],
- ):
- self._data = data
- self._signers = signers
- self._additional_certs = additional_certs
- def set_data(self, data: bytes) -> "PKCS7SignatureBuilder":
- _check_byteslike("data", data)
- if self._data is not None:
- raise ValueError("data may only be set once")
- return PKCS7SignatureBuilder(data, self._signers)
- def add_signer(
- self,
- certificate: x509.Certificate,
- private_key: _ALLOWED_PRIVATE_KEY_TYPES,
- hash_algorithm: _ALLOWED_PKCS7_HASH_TYPES,
- ) -> "PKCS7SignatureBuilder":
- if not isinstance(
- hash_algorithm,
- (
- hashes.SHA1,
- hashes.SHA224,
- hashes.SHA256,
- hashes.SHA384,
- hashes.SHA512,
- ),
- ):
- raise TypeError(
- "hash_algorithm must be one of hashes.SHA1, SHA224, "
- "SHA256, SHA384, or SHA512"
- )
- if not isinstance(certificate, x509.Certificate):
- raise TypeError("certificate must be a x509.Certificate")
- if not isinstance(
- private_key, (rsa.RSAPrivateKey, ec.EllipticCurvePrivateKey)
- ):
- raise TypeError("Only RSA & EC keys are supported at this time.")
- return PKCS7SignatureBuilder(
- self._data,
- self._signers + [(certificate, private_key, hash_algorithm)],
- )
- def add_certificate(
- self, certificate: x509.Certificate
- ) -> "PKCS7SignatureBuilder":
- if not isinstance(certificate, x509.Certificate):
- raise TypeError("certificate must be a x509.Certificate")
- return PKCS7SignatureBuilder(
- self._data, self._signers, self._additional_certs + [certificate]
- )
- def sign(
- self,
- encoding: serialization.Encoding,
- options: typing.Iterable[PKCS7Options],
- backend: typing.Any = None,
- ) -> bytes:
- if len(self._signers) == 0:
- raise ValueError("Must have at least one signer")
- if self._data is None:
- raise ValueError("You must add data to sign")
- options = list(options)
- if not all(isinstance(x, PKCS7Options) for x in options):
- raise ValueError("options must be from the PKCS7Options enum")
- if encoding not in (
- serialization.Encoding.PEM,
- serialization.Encoding.DER,
- serialization.Encoding.SMIME,
- ):
- raise ValueError(
- "Must be PEM, DER, or SMIME from the Encoding enum"
- )
- # Text is a meaningless option unless it is accompanied by
- # DetachedSignature
- if (
- PKCS7Options.Text in options
- and PKCS7Options.DetachedSignature not in options
- ):
- raise ValueError(
- "When passing the Text option you must also pass "
- "DetachedSignature"
- )
- if PKCS7Options.Text in options and encoding in (
- serialization.Encoding.DER,
- serialization.Encoding.PEM,
- ):
- raise ValueError(
- "The Text option is only available for SMIME serialization"
- )
- # No attributes implies no capabilities so we'll error if you try to
- # pass both.
- if (
- PKCS7Options.NoAttributes in options
- and PKCS7Options.NoCapabilities in options
- ):
- raise ValueError(
- "NoAttributes is a superset of NoCapabilities. Do not pass "
- "both values."
- )
- from cryptography.hazmat.backends.openssl.backend import (
- backend as ossl,
- )
- return ossl.pkcs7_sign(self, encoding, options)
|