Skip to content

Commit 085d1e4

Browse files
authored
allow additional certificates to be added to a pkcs7 (#5498)
* allow additional certificates to be added to a pkcs7 * be more verbose about what these additional certs might be used for * missing test
1 parent 95c4f68 commit 085d1e4

File tree

4 files changed

+77
-2
lines changed

4 files changed

+77
-2
lines changed

docs/hazmat/primitives/asymmetric/serialization.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,14 @@ contain certificates, CRLs, and much more. PKCS7 files commonly have a ``p7b``,
647647
:class:`~cryptography.hazmat.primitives.hashes.SHA384`, or
648648
:class:`~cryptography.hazmat.primitives.hashes.SHA512`.
649649

650+
.. method:: add_certificate(certificate)
651+
652+
Add an additional certificate (typically used to help build a
653+
verification chain) to the PKCS7 structure. This method may
654+
be called multiple times to add as many certificates as desired.
655+
656+
:param certificate: The :class:`~cryptography.x509.Certificate` to add.
657+
650658
.. method:: sign(encoding, options, backend=None)
651659

652660
:param encoding: :attr:`~cryptography.hazmat.primitives.serialization.Encoding.PEM`,

src/cryptography/hazmat/backends/openssl/backend.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2695,6 +2695,15 @@ def pkcs7_sign(self, builder, encoding, options):
26952695
init_flags = self._lib.PKCS7_PARTIAL
26962696
final_flags = 0
26972697

2698+
if len(builder._additional_certs) == 0:
2699+
certs = self._ffi.NULL
2700+
else:
2701+
certs = self._lib.sk_X509_new_null()
2702+
certs = self._ffi.gc(certs, self._lib.sk_X509_free)
2703+
for cert in builder._additional_certs:
2704+
res = self._lib.sk_X509_push(certs, cert._x509)
2705+
self.openssl_assert(res >= 1)
2706+
26982707
if pkcs7.PKCS7Options.DetachedSignature in options:
26992708
# Don't embed the data in the PKCS7 structure
27002709
init_flags |= self._lib.PKCS7_DETACHED
@@ -2705,7 +2714,7 @@ def pkcs7_sign(self, builder, encoding, options):
27052714
p7 = self._lib.PKCS7_sign(
27062715
self._ffi.NULL,
27072716
self._ffi.NULL,
2708-
self._ffi.NULL,
2717+
certs,
27092718
self._ffi.NULL,
27102719
init_flags,
27112720
)

src/cryptography/hazmat/primitives/serialization/pkcs7.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,10 @@ def load_der_pkcs7_certificates(data):
2424

2525

2626
class PKCS7SignatureBuilder(object):
27-
def __init__(self, data=None, signers=[]):
27+
def __init__(self, data=None, signers=[], additional_certs=[]):
2828
self._data = data
2929
self._signers = signers
30+
self._additional_certs = additional_certs
3031

3132
def set_data(self, data):
3233
_check_byteslike("data", data)
@@ -63,6 +64,14 @@ def add_signer(self, certificate, private_key, hash_algorithm):
6364
self._signers + [(certificate, private_key, hash_algorithm)],
6465
)
6566

67+
def add_certificate(self, certificate):
68+
if not isinstance(certificate, x509.Certificate):
69+
raise TypeError("certificate must be a x509.Certificate")
70+
71+
return PKCS7SignatureBuilder(
72+
self._data, self._signers, self._additional_certs + [certificate]
73+
)
74+
6675
def sign(self, encoding, options, backend=None):
6776
if len(self._signers) == 0:
6877
raise ValueError("Must have at least one signer")

tests/hazmat/primitives/test_pkcs7.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -607,3 +607,52 @@ def test_multiple_signers_different_hash_algs(self, backend):
607607
options,
608608
backend,
609609
)
610+
611+
def test_add_additional_cert_not_a_cert(self, backend):
612+
with pytest.raises(TypeError):
613+
pkcs7.PKCS7SignatureBuilder().add_certificate(b"notacert")
614+
615+
def test_add_additional_cert(self, backend):
616+
data = b"hello world"
617+
cert, key = _load_cert_key()
618+
rsa_cert = load_vectors_from_file(
619+
os.path.join("x509", "custom", "ca", "rsa_ca.pem"),
620+
loader=lambda pemfile: x509.load_pem_x509_certificate(
621+
pemfile.read()
622+
),
623+
mode="rb",
624+
)
625+
builder = (
626+
pkcs7.PKCS7SignatureBuilder()
627+
.set_data(data)
628+
.add_signer(cert, key, hashes.SHA384())
629+
.add_certificate(rsa_cert)
630+
)
631+
options = []
632+
sig = builder.sign(serialization.Encoding.DER, options)
633+
assert (
634+
sig.count(rsa_cert.public_bytes(serialization.Encoding.DER)) == 1
635+
)
636+
637+
def test_add_multiple_additional_certs(self, backend):
638+
data = b"hello world"
639+
cert, key = _load_cert_key()
640+
rsa_cert = load_vectors_from_file(
641+
os.path.join("x509", "custom", "ca", "rsa_ca.pem"),
642+
loader=lambda pemfile: x509.load_pem_x509_certificate(
643+
pemfile.read()
644+
),
645+
mode="rb",
646+
)
647+
builder = (
648+
pkcs7.PKCS7SignatureBuilder()
649+
.set_data(data)
650+
.add_signer(cert, key, hashes.SHA384())
651+
.add_certificate(rsa_cert)
652+
.add_certificate(rsa_cert)
653+
)
654+
options = []
655+
sig = builder.sign(serialization.Encoding.DER, options)
656+
assert (
657+
sig.count(rsa_cert.public_bytes(serialization.Encoding.DER)) == 2
658+
)

0 commit comments

Comments
 (0)