def finalize(self, digest_algorithm): """Build all data structures from the given parameters and return the top level contentInfo. Returns: ContentInfo: The PKIMessage """ pkcs_pki_envelope = self._pki_envelope pkienvelope_content_info = ContentInfo({ 'content_type': ContentType(u'enveloped_data'), 'content': pkcs_pki_envelope, }) # NOTE: This might not be needed for the degenerate CertRep encap_info = ContentInfo({ 'content_type': ContentType(u'data'), 'content': pkienvelope_content_info.dump() }) # Calculate digest on encrypted content + signed_attrs d = digest_for_data(algorithm=digest_algorithm, data=pkienvelope_content_info.dump()) # Now start building SignedData signer_infos = self._build_signerinfos(pkienvelope_content_info.dump(), d, self._cms_attributes) certificates = self._certificates da_id = DigestAlgorithmId(six.text_type(digest_algorithm)) da = DigestAlgorithm({u'algorithm': da_id}) das = DigestAlgorithms([da]) sd = SignedData({ 'version': 1, 'certificates': certificates, 'signer_infos': signer_infos, 'digest_algorithms': das, 'encap_content_info': encap_info, # should point to type data + content contentinfo }) ci = ContentInfo({ 'content_type': ContentType(u'signed_data'), 'content': sd, }) return ci
def finalize(self) -> ContentInfo: """Build all data structures from the given parameters and return the top level contentInfo. Returns: ContentInfo: The PKIMessage """ pkcs_pki_envelope = self._pki_envelope pkienvelope_content_info = ContentInfo({ 'content_type': ContentType('enveloped_data'), 'content': pkcs_pki_envelope, }) # NOTE: This might not be needed for the degenerate CertRep encap_info = ContentInfo({ 'content_type': ContentType('data'), 'content': pkienvelope_content_info.dump() }) # encap_info_degen = ContentInfo({ # 'content_type': ContentType('data'), # 'content': pkcs_pki_envelope.dump() # }) # Calculate digest on encrypted content + signed_attrs #digest = hashes.Hash(hashes.SHA256(), backend=default_backend()) digest = hashes.Hash(hashes.SHA512(), backend=default_backend()) #digest = hashes.Hash(hashes.SHA1(), backend=default_backend()) # digest.update(pkcs_pki_envelope.dump()) digest.update(pkienvelope_content_info.dump()) d = digest.finalize() # Now start building SignedData # signer_infos = self._build_signerinfos(pkcs_pki_envelope.dump(), d, self._cms_attributes) signer_infos = self._build_signerinfos(pkienvelope_content_info.dump(), d, self._cms_attributes) certificates = self._certificates #da_id = DigestAlgorithmId('sha256') # SHA-1 works for macOS # da_id = DigestAlgorithmId('sha1') da_id = DigestAlgorithmId('sha512') da = DigestAlgorithm({'algorithm': da_id}) das = DigestAlgorithms([da]) sd = SignedData({ 'version': 1, 'certificates': certificates, 'signer_infos': signer_infos, 'digest_algorithms': das, 'encap_content_info': encap_info, # should point to type data + content contentinfo }) ci = ContentInfo({ 'content_type': ContentType('signed_data'), 'content': sd, }) return ci
def sign(self): h = hashes.Hash(hashes.SHA256(), backend=default_backend()) h.update(self._content_mime.as_bytes()) message_digest = h.finalize() cs = CertificateSet() cs.append(load(self._certificate.public_bytes(Encoding.DER))) for ca_cert in self._ca: cs.append(load(ca_cert.public_bytes(Encoding.DER))) ec = ContentInfo({ 'content_type': ContentType('data'), }) sident = SignerIdentifier({ 'issuer_and_serial_number': IssuerAndSerialNumber({ 'issuer': load(self._issuer_name.public_bytes(default_backend())), 'serial_number': self._cert_serial, }) }) certv2 = ESSCertIDv2({ 'hash_algorithm': DigestAlgorithm({'algorithm': DigestAlgorithmId('sha256')}), 'cert_hash': OctetString(self._certificate.fingerprint(hashes.SHA256())), 'issuer_serial': IssuerSerial({ 'issuer': load(self._issuer_name.public_bytes(default_backend())), 'serial_number': self._cert_serial, }), }) now = datetime.now().replace(microsecond=0, tzinfo=pytz.utc) # .isoformat() sattrs = CMSAttributes({ CMSAttribute({ 'type': CMSAttributeType('content_type'), 'values': ["data"] }), CMSAttribute({ 'type': CMSAttributeType('message_digest'), 'values': [message_digest] }), CMSAttribute({ 'type': CMSAttributeType('signing_time'), 'values': (Time({'utc_time': UTCTime(now)}), ) }), CMSAttribute({ 'type': CMSAttributeType('signing_certificate_v2'), 'values': [SigningCertificateV2({'certs': (certv2, )})] }) }) signature = self._private_key.sign(sattrs.dump(), padding.PKCS1v15(), hashes.SHA256()) # si = SignerInfo({ 'version': 'v1', 'sid': sident, 'digest_algorithm': DigestAlgorithm({'algorithm': DigestAlgorithmId('sha256')}), 'signed_attrs': sattrs, 'signature_algorithm': SignedDigestAlgorithm( {'algorithm': SignedDigestAlgorithmId('rsassa_pkcs1v15')}), 'signature': signature, }) da = DigestAlgorithms( (DigestAlgorithm({'algorithm': DigestAlgorithmId('sha256')}), )) signed_data = SignedData({ 'version': 'v1', 'encap_content_info': ec, 'certificates': cs, 'digest_algorithms': da, 'signer_infos': SignerInfos((si, )) }) ci = ContentInfo({ 'content_type': ContentType('signed_data'), 'content': signed_data }) self._signature_mime = MIMEApplication(ci.dump(), _subtype="pkcs7-signature", name="smime.p7s", policy=email.policy.SMTPUTF8) self._signature_mime.add_header('Content-Disposition', 'attachment; filename=smime.p7s') super(CADESMIMESignature, self).attach(self._content_mime) super(CADESMIMESignature, self).attach(self._signature_mime)
def sign(self): h = hashes.Hash(hashes.SHA256(), backend=default_backend()) h.update(self._content_mime.as_bytes()) message_digest = h.finalize() cs = CertificateSet() cs.append(load(self._certificate.public_bytes(Encoding.DER))) for ca_cert in self._ca: cs.append(load(ca_cert.public_bytes(Encoding.DER))) ec = EncapsulatedContentInfo({ 'content_type': ContentType('data'), 'content': ParsableOctetString(self._content_mime.as_bytes()) }) sident = SignerIdentifier({ 'issuer_and_serial_number': IssuerAndSerialNumber({ 'issuer': load(self._issuer_name.public_bytes(default_backend())), 'serial_number': self._cert_serial, }) }) certv2 = ESSCertIDv2({ 'hash_algorithm': DigestAlgorithm({'algorithm': DigestAlgorithmId('sha256')}), 'cert_hash': OctetString(self._certificate.fingerprint(hashes.SHA256())), 'issuer_serial': IssuerSerial({ 'issuer': load( self._issuer_name.public_bytes(default_backend()) ), #[GeneralName({'directory_name': self._issuer_name.public_bytes(default_backend())})], 'serial_number': self._cert_serial, }), }) now = datetime.now().replace(microsecond=0, tzinfo=pytz.utc) sattrs = CMSAttributes({ CMSAttribute({ 'type': CMSAttributeType('content_type'), 'values': ["data"] }), CMSAttribute({ 'type': CMSAttributeType('message_digest'), 'values': [message_digest] }), CMSAttribute({ 'type': CMSAttributeType('signing_time'), 'values': (Time({'utc_time': UTCTime(now)}), ) }), # isti k v CMSAttribute({ 'type': CMSAttributeType('signing_certificate_v2'), 'values': [SigningCertificateV2({'certs': (certv2, )})] }) }) signature = self._private_key.sign(sattrs.dump(), padding.PKCS1v15(), hashes.SHA256()) si = SignerInfo({ 'version': 'v1', 'sid': sident, 'digest_algorithm': DigestAlgorithm({'algorithm': DigestAlgorithmId('sha256')}), 'signed_attrs': sattrs, 'signature_algorithm': SignedDigestAlgorithm( {'algorithm': SignedDigestAlgorithmId('rsassa_pkcs1v15')}), 'signature': signature, }) da = DigestAlgorithms( (DigestAlgorithm({'algorithm': DigestAlgorithmId('sha256')}), )) signed_data = SignedData({ 'version': 'v3', 'encap_content_info': ec, 'certificates': cs, 'digest_algorithms': da, 'signer_infos': SignerInfos((si, )) }) ci = ContentInfo({ 'content_type': ContentType('signed_data'), 'content': signed_data }) self.set_payload(ci.dump()) encode_base64(self)
def embed_payload_with_cms(pdf_writer: BasePdfFileWriter, file_spec_string: str, payload: embed.EmbeddedFileObject, cms_obj: cms.ContentInfo, extension='.sig', file_name: Optional[str] = None, file_spec_kwargs=None, cms_file_spec_kwargs=None): """ Embed some data as an embedded file stream into a PDF, and associate it with a CMS object. The resulting CMS object will also be turned into an embedded file, and associated with the original payload through a related file relationship. This can be used to bundle (non-PDF) detached signatures with PDF attachments, for example. .. versionadded:: 0.7.0 :param pdf_writer: The PDF writer to use. :param file_spec_string: See :attr:`~pyhanko.pdf_utils.embed.FileSpec.file_spec_string` in :class:`~pyhanko.pdf_utils.embed.FileSpec`. :param payload: Payload object. :param cms_obj: CMS object pertaining to the payload. :param extension: File extension to use for the CMS attachment. :param file_name: See :attr:`~pyhanko.pdf_utils.embed.FileSpec.file_name` in :class:`~pyhanko.pdf_utils.embed.FileSpec`. :param file_spec_kwargs: Extra arguments to pass to the :class:`~pyhanko.pdf_utils.embed.FileSpec` constructor for the main attachment specification. :param cms_file_spec_kwargs: Extra arguments to pass to the :class:`~pyhanko.pdf_utils.embed.FileSpec` constructor for the CMS attachment specification. """ # prepare an embedded file object for the signature now = datetime.now(tz=tzlocal.get_localzone()) cms_ef_obj = embed.EmbeddedFileObject.from_file_data( pdf_writer=pdf_writer, data=cms_obj.dump(), compress=False, mime_type='application/pkcs7-mime', params=embed.EmbeddedFileParams(creation_date=now, modification_date=now)) # replace extension cms_data_f = file_spec_string.rsplit('.', 1)[0] + extension # deal with new-style Unicode file names cms_data_uf = uf_related_files = None if file_name is not None: cms_data_uf = file_name.rsplit('.', 1)[0] + extension uf_related_files = [ embed.RelatedFileSpec(cms_data_uf, embedded_data=cms_ef_obj) ] spec = embed.FileSpec( file_spec_string=file_spec_string, file_name=file_name, embedded_data=payload, f_related_files=[ embed.RelatedFileSpec(cms_data_f, embedded_data=cms_ef_obj) ], uf_related_files=uf_related_files, **(file_spec_kwargs or {}), ) embed.embed_file(pdf_writer, spec) # also embed the CMS data as a standalone attachment cms_spec = embed.FileSpec(file_spec_string=cms_data_f, file_name=cms_data_uf, embedded_data=cms_ef_obj, **(cms_file_spec_kwargs or {})) embed.embed_file(pdf_writer, cms_spec)