Skip to content

Commit

Permalink
allow additional certificates to be added to a pkcs7 (#5498)
Browse files Browse the repository at this point in the history
* allow additional certificates to be added to a pkcs7

* be more verbose about what these additional certs might be used for

* missing test
  • Loading branch information
reaperhulk committed Oct 25, 2020
1 parent 95c4f68 commit 085d1e4
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 2 deletions.
8 changes: 8 additions & 0 deletions docs/hazmat/primitives/asymmetric/serialization.rst
Expand Up @@ -647,6 +647,14 @@ contain certificates, CRLs, and much more. PKCS7 files commonly have a ``p7b``,
:class:`~cryptography.hazmat.primitives.hashes.SHA384`, or
:class:`~cryptography.hazmat.primitives.hashes.SHA512`.

.. method:: add_certificate(certificate)

Add an additional certificate (typically used to help build a
verification chain) to the PKCS7 structure. This method may
be called multiple times to add as many certificates as desired.

:param certificate: The :class:`~cryptography.x509.Certificate` to add.

.. method:: sign(encoding, options, backend=None)

:param encoding: :attr:`~cryptography.hazmat.primitives.serialization.Encoding.PEM`,
Expand Down
11 changes: 10 additions & 1 deletion src/cryptography/hazmat/backends/openssl/backend.py
Expand Up @@ -2695,6 +2695,15 @@ def pkcs7_sign(self, builder, encoding, options):
init_flags = self._lib.PKCS7_PARTIAL
final_flags = 0

if len(builder._additional_certs) == 0:
certs = self._ffi.NULL
else:
certs = self._lib.sk_X509_new_null()
certs = self._ffi.gc(certs, self._lib.sk_X509_free)
for cert in builder._additional_certs:
res = self._lib.sk_X509_push(certs, cert._x509)
self.openssl_assert(res >= 1)

if pkcs7.PKCS7Options.DetachedSignature in options:
# Don't embed the data in the PKCS7 structure
init_flags |= self._lib.PKCS7_DETACHED
Expand All @@ -2705,7 +2714,7 @@ def pkcs7_sign(self, builder, encoding, options):
p7 = self._lib.PKCS7_sign(
self._ffi.NULL,
self._ffi.NULL,
self._ffi.NULL,
certs,
self._ffi.NULL,
init_flags,
)
Expand Down
11 changes: 10 additions & 1 deletion src/cryptography/hazmat/primitives/serialization/pkcs7.py
Expand Up @@ -24,9 +24,10 @@ def load_der_pkcs7_certificates(data):


class PKCS7SignatureBuilder(object):
def __init__(self, data=None, signers=[]):
def __init__(self, data=None, signers=[], additional_certs=[]):
self._data = data
self._signers = signers
self._additional_certs = additional_certs

def set_data(self, data):
_check_byteslike("data", data)
Expand Down Expand Up @@ -63,6 +64,14 @@ def add_signer(self, certificate, private_key, hash_algorithm):
self._signers + [(certificate, private_key, hash_algorithm)],
)

def add_certificate(self, certificate):
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, options, backend=None):
if len(self._signers) == 0:
raise ValueError("Must have at least one signer")
Expand Down
49 changes: 49 additions & 0 deletions tests/hazmat/primitives/test_pkcs7.py
Expand Up @@ -607,3 +607,52 @@ def test_multiple_signers_different_hash_algs(self, backend):
options,
backend,
)

def test_add_additional_cert_not_a_cert(self, backend):
with pytest.raises(TypeError):
pkcs7.PKCS7SignatureBuilder().add_certificate(b"notacert")

def test_add_additional_cert(self, backend):
data = b"hello world"
cert, key = _load_cert_key()
rsa_cert = load_vectors_from_file(
os.path.join("x509", "custom", "ca", "rsa_ca.pem"),
loader=lambda pemfile: x509.load_pem_x509_certificate(
pemfile.read()
),
mode="rb",
)
builder = (
pkcs7.PKCS7SignatureBuilder()
.set_data(data)
.add_signer(cert, key, hashes.SHA384())
.add_certificate(rsa_cert)
)
options = []
sig = builder.sign(serialization.Encoding.DER, options)
assert (
sig.count(rsa_cert.public_bytes(serialization.Encoding.DER)) == 1
)

def test_add_multiple_additional_certs(self, backend):
data = b"hello world"
cert, key = _load_cert_key()
rsa_cert = load_vectors_from_file(
os.path.join("x509", "custom", "ca", "rsa_ca.pem"),
loader=lambda pemfile: x509.load_pem_x509_certificate(
pemfile.read()
),
mode="rb",
)
builder = (
pkcs7.PKCS7SignatureBuilder()
.set_data(data)
.add_signer(cert, key, hashes.SHA384())
.add_certificate(rsa_cert)
.add_certificate(rsa_cert)
)
options = []
sig = builder.sign(serialization.Encoding.DER, options)
assert (
sig.count(rsa_cert.public_bytes(serialization.Encoding.DER)) == 2
)

0 comments on commit 085d1e4

Please sign in to comment.