def _sort_attributes(attrs_in: CMSAttributes) -> CMSAttributes: """ Sort the authenticated attributes for signing by re-encoding them, asn1crypto takes care of the actual sorting of the set. """ attrs_out = CMSAttributes() for attrval in attrs_in: attrs_out.append(attrval) return attrs_out
def _build_cmsattributes(self) -> CMSAttributes: """Finalize the set of CMS Attributes and return the collection. Returns: CMSAttributes: All of the added CMS attributes """ return CMSAttributes(value=self._cms_attributes)
def make_signed_attrs(digest: bytes, hash_type: int) -> CMSAttributes: content_type = CMSAttribute({ "type": CMSAttributeType.unmap("content_type"), "values": [ContentType.unmap("data")], }) time_now = UTCTime() time_now.set(datetime.now(timezone.utc)) signing_time = CMSAttribute({ "type": CMSAttributeType.unmap("signing_time"), "values": [time_now] }) message_digest = CMSAttribute({ "type": CMSAttributeType.unmap("message_digest"), "values": [OctetString(digest)], }) ha_v1 = make_hash_agility_v1(digest) ha_v2 = make_hash_agility_v2(digest, hash_type) return CMSAttributes( [content_type, signing_time, message_digest, ha_v1, ha_v2])
def make_signature(self): assert self.sig.code_dir_blob # Redo the code hashes self._set_code_hashes() # Make the signature signed_attrs: CMSAttributes = make_signed_attrs( self.sig.code_dir_blob.get_hash(self.hash_type), self.hash_type) actual_privkey = load_private_key(self.privkey) signature = rsa_pkcs1v15_sign(actual_privkey, signed_attrs.dump(), self.hash_type_str) # Get the timestamp from Apple digest = get_hash(signature, self.hash_type) tst = CMSAttribute({ "type": CMSAttributeType("signature_time_stamp_token"), "values": [get_timestamp_token(digest, self.hash_type)], }) # Make the CMS self.sig.sig_blob = SignatureBlob() self.sig.sig_blob.cms = make_cms(self.cert, self.hash_type, signed_attrs, signature, CMSAttributes([tst])) # Get the CodeSignature section. It should be the last in the binary cs_sec = self.macho.sect[-1] assert cs_sec == self.get_linkedit_segment().sect[-1] assert isinstance(cs_sec, CodeSignature) sig_cmd = self.get_sig_command() # Serialize the signature f = BytesIO() self.sig.serialize(f) f.write((sig_cmd.datasize - f.tell()) * b"\x00") if self.detach_target: target_dir = os.path.join(self.detach_target, "Contents", "MacOS") os.makedirs(target_dir, exist_ok=True) target_file = os.path.join( target_dir, os.path.basename(self.filename) + f".{CPU_NAMES[self.macho.Mhdr.cputype]}sign", ) with open(target_file, "wb") as tf: tf.write(f.getvalue()) self.files_modified.append(target_file) else: # Set the section's content to be the signature cs_sec.content = StrPatchwork(f.getvalue())
def sign(self, data: bytes, content_type: ContentType, content_digest: bytes, cms_attributes: List[CMSAttribute]) -> SignerInfo: """Generate a signature encrypted with the signer's private key and return the SignerInfo.""" # The CMS standard requires that the content-type authenticatedAttribute and the message-digest # attribute must be present if any authenticatedAttribute exists at all. self.signed_attributes = cms_attributes self.signed_attributes.insert(0, CMSAttribute({ 'type': 'signing_time', 'values': [GeneralizedTime(datetime.datetime.utcnow())] })) self.signed_attributes.insert(0, CMSAttribute({ 'type': 'message_digest', 'values': [OctetString(content_digest)], })) # This refers to whatever the content of EncapsulatedContentInfo is self.signed_attributes.insert(0, CMSAttribute({ 'type': 'content_type', 'values': [content_type], })) cms_attributes = CMSAttributes(self.signed_attributes) # NOTE: no need to calculate this digest as .signer() does the hashing # RFC5652 # The message digest is # computed on either the content being signed or the content # together with the signed attributes using the process described in # Section 5.4. # digest = hashes.Hash(hashes.SHA256(), backend=default_backend()) # the initial input is the encapContentInfo eContent OCTET STRING # RFC5652 Section 5.4 - When the field (signed_attrs) is present, however, the result is the message # digest of the complete DER encoding of the SignedAttrs value # contained in the signedAttrs field. # NOTE: it is not clear whether data is included #digest.update(data) # digest.update(cms_attributes.dump()) # d = digest.finalize() # Make DigestInfo from result # NOTE: It is not clear whether this applies: RFC5652 - Section 5.5. # digest_info = DigestInfo({ # 'digest_algorithm': self.digest_algorithm, # 'digest': d, # }) # Get the RSA key to sign the digestinfo digest_function = { 'sha1': hashes.SHA1, # macOS 'sha256': hashes.SHA256, 'sha512': hashes.SHA512 }[self.digest_algorithm_id.native] signer = self.private_key.signer( asympad.PKCS1v15(), digest_function(), ) # NOTE: this is not the digest `d` above because crypto.io already hashes stuff for us!! signer.update(cms_attributes.dump()) signature = signer.finalize() signer_info = SignerInfo({ # Version must be 1 if signer uses IssuerAndSerialNumber as sid 'version': CMSVersion(1), 'sid': self.sid, 'digest_algorithm': self.digest_algorithm, 'signed_attrs': cms_attributes, # Referred to as ``digestEncryptionAlgorithm`` in the RFC 'signature_algorithm': self.signed_digest_algorithm, # Referred to as ``encryptedDigest`` in the RFC 'signature': OctetString(signature), }) return signer_info
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 sign(self, data, content_type, content_digest, cms_attributes): """Generate a signature encrypted with the signer's private key and return the SignerInfo.""" # The CMS standard requires that the content-type authenticatedAttribute and the message-digest # attribute must be present if any authenticatedAttribute exists at all. self.signed_attributes = cms_attributes # NDES does not even include this # self.signed_attributes.insert(0, CMSAttribute({ # 'type': 'signing_time', # 'values': [GeneralizedTime(datetime.datetime.utcnow())] # })) self.signed_attributes.insert( 0, CMSAttribute({ 'type': u'message_digest', 'values': [OctetString(content_digest)], })) # This refers to whatever the content of EncapsulatedContentInfo is self.signed_attributes.insert( 0, CMSAttribute({ 'type': u'content_type', 'values': [content_type], })) cms_attributes = CMSAttributes(self.signed_attributes) # NOTE: no need to calculate this digest as .signer() does the hashing # RFC5652 # The message digest is # computed on either the content being signed or the content # together with the signed attributes using the process described in # Section 5.4. # digest = hashes.Hash(hashes.SHA256(), backend=default_backend()) # the initial input is the encapContentInfo eContent OCTET STRING # RFC5652 Section 5.4 - When the field (signed_attrs) is present, however, the result is the message # digest of the complete DER encoding of the SignedAttrs value # contained in the signedAttrs field. # NOTE: it is not clear whether data is included #digest.update(data) # digest.update(cms_attributes.dump()) # d = digest.finalize() # Make DigestInfo from result # NOTE: It is not clear whether this applies: RFC5652 - Section 5.5. # digest_info = DigestInfo({ # 'digest_algorithm': self.digest_algorithm, # 'digest': d, # }) signature = self.private_key.sign( data=cms_attributes.dump(), padding_type='pkcs', algorithm=self.digest_algorithm_id.native) signer_info = SignerInfo({ # Version must be 1 if signer uses IssuerAndSerialNumber as sid 'version': CMSVersion(1), 'sid': self.sid, 'digest_algorithm': self.digest_algorithm, 'signed_attrs': cms_attributes, # Referred to as ``digestEncryptionAlgorithm`` in the RFC 'signature_algorithm': self.signed_digest_algorithm, # Referred to as ``encryptedDigest`` in the RFC 'signature': OctetString(signature), }) return signer_info