def create_degenerate_pkcs7(*certificates: List[x509.Certificate]) -> ContentInfo: """Produce a PKCS#7 Degenerate case. The degenerate case is a SignedData content type in which there are no signers. Certificates are disseminated via the ``certificates`` attribute. Args: *certificates (List[x509.Certificate]): The certificates to attach to the degenerate pkcs#7 payload. The first must always be the issued certificate. Returns: ContentInfo: The ContentInfo containing a SignedData structure. """ certificates_der = [c.public_bytes(serialization.Encoding.DER) for c in certificates] certificates_asn1 = [parse_certificate(der_bytes) for der_bytes in certificates_der] # draft-gutmann-scep 3.4. content type must be omitted empty = ContentInfo({ 'content_type': ContentType('data') }) sd_certificates = CertificateSet([CertificateChoices('certificate', asn1) for asn1 in certificates_asn1]) sd = SignedData({ 'version': CMSVersion(1), 'encap_content_info': empty, 'digest_algorithms': DigestAlgorithms([]), 'certificates': sd_certificates, 'signer_infos': SignerInfos([]), 'crls': RevocationInfoChoices([]), }) return ContentInfo({ 'content_type': ContentType('signed_data'), 'content': sd, })
def create_degenerate_certificate(certificate: x509.Certificate) -> ContentInfo: """Produce a PKCS#7 Degenerate case with a single certificate. Args: certificate (x509.Certificate): The certificate to attach to the degenerate pkcs#7 payload. Returns: ContentInfo: The ContentInfo containing a SignedData structure. """ der_bytes = certificate.public_bytes( serialization.Encoding.DER ) asn1cert = parse_certificate(der_bytes) empty = ContentInfo({ 'content_type': ContentType('data') }) sd = SignedData({ 'version': CMSVersion(1), 'encap_content_info': empty, 'digest_algorithms': DigestAlgorithms([]), 'certificates': CertificateSet([CertificateChoices('certificate', asn1cert)]), 'signer_infos': SignerInfos([]), 'crls': RevocationInfoChoices([]), }) return ContentInfo({ 'content_type': ContentType('signed_data'), 'content': sd, })
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 make_cms( cert: Certificate, hash_type: int, signed_attrs: CMSAttributes, sig: bytes, unsigned_attrs: Optional[CMSAttributes], ) -> ContentInfo: sid = SignerIdentifier( "issuer_and_serial_number", IssuerAndSerialNumber({ "issuer": cert["tbs_certificate"]["issuer"], "serial_number": cert["tbs_certificate"]["serial_number"], }), ) dg_algo = DigestAlgorithm({"algorithm": _get_digest_algo(hash_type)}) sig_algo = SignedDigestAlgorithm( {"algorithm": SignedDigestAlgorithmId("rsassa_pkcs1v15")}) sig_info = SignerInfo({ "version": CMSVersion(1), "sid": sid, "digest_algorithm": dg_algo, "signed_attrs": signed_attrs, "signature_algorithm": sig_algo, "signature": OctetString(sig), "unsigned_attrs": unsigned_attrs, }) certs = make_certificate_chain(cert) signed_data = SignedData({ "version": CMSVersion(1), "digest_algorithms": [dg_algo], "encap_content_info": ContentInfo({"content_type": ContentType("data")}), "certificates": certs, "signer_infos": [sig_info], }) return ContentInfo({ "content_type": ContentType.unmap("signed_data"), "content": signed_data })
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 decrypt_pk_dh(data, diffieHellmanExchange): try: rep = SPNEGO_PKINIT_AS_REP.load(bytes.fromhex(data)).native except: krb_message = KerberosResponse.load(bytes.fromhex(data)) raise KerberosError(krb_message) relevantPadata = None for padata in rep['Kerberos']['padata']: if padata['padata-type'] == 17: relevantPadata = PA_PK_AS_REP.load(padata['padata-value']).native break if not relevantPadata: raise Exception('No PAdata found with type 17') keyinfo = SignedData.load( relevantPadata['dhSignedData']).native['encap_content_info'] if keyinfo['content_type'] != '1.3.6.1.5.2.3.2': raise Exception('Keyinfo content type unexpected value') authdata = KDCDHKeyInfo.load(keyinfo['content']).native pubkey = int( ''.join(['1'] + [str(x) for x in authdata['subjectPublicKey']]), 2) pubkey = int.from_bytes(core.BitString( authdata['subjectPublicKey']).dump()[7:], 'big', signed=False) shared_key = diffieHellmanExchange.exchange(pubkey) server_nonce = relevantPadata['serverDHNonce'] fullKey = shared_key + diffieHellmanExchange.dh_nonce + server_nonce etype = rep['Kerberos']['enc-part']['etype'] cipher = _enctype_table[etype] if etype == Enctype.AES256: t_key = truncate(fullKey, 32) elif etype == Enctype.AES128: t_key = truncate(fullKey, 16) elif etype == Enctype.RC4: raise NotImplementedError( 'RC4 key truncation documentation missing. it is different from AES' ) key = Key(cipher.enctype, t_key) enc_data = rep['Kerberos']['enc-part']['cipher'] dec_data = cipher.decrypt(key, 3, enc_data) encasrep = EncASRepPart.load(dec_data).native cipher = _enctype_table[int(encasrep['key']['keytype'])] session_key = Key(cipher.enctype, encasrep['key']['keyvalue']) return session_key, cipher, rep # remove Octet String manualy padata = str(rep['padata'][0]['padata-value']).encode('hex') parsedPadata = decode(padata.decode('hex'), asn1Spec=AS_REP_Padata())[0] decoded = parsedPadata['DHRepInfo']['dhSignedData'] kdcSignedDataResponse = decode(decoded, asn1Spec=SignedData())[0] kdcDHKeyInfo = str(kdcSignedDataResponse['encapContentInfo'] ['id-pkinit-authData-value']).encode('hex') d = decode(kdcDHKeyInfo.decode('hex'), asn1Spec=KDCDHKeyInfo())[0] dcPublicKey = int(encode(d['subjectPublicKey']).encode('hex')[20:], 16) dcPublicNumbers = dh.DHPublicNumbers(dcPublicKey, diffieHellmanExchange[2]) backend = default_backend() dcPublicKey = backend.load_dh_public_numbers(dcPublicNumbers) shared_key = diffieHellmanExchange[1].exchange(dcPublicKey) sharedHexKey = shared_key.encode('hex') clientDHNonce = '6B328FA66EEBDFD3D69ED34E5007776AB30832A2ED1DCB1699781BFE0BEDF87A' serverDHNonce = encode( parsedPadata['DHRepInfo']['encKeyPack']).encode('hex')[8:] fullKey = sharedHexKey + clientDHNonce + serverDHNonce etype = rep['enc-part']['etype'] cipher = _enctype_table[etype] if etype == Enctype.AES256: truncateKey = truncate(fullKey, 32) key = Key(cipher.enctype, truncateKey) elif etype == Enctype.AES128: truncateKey = truncate(fullKey, 16) key = Key(cipher.enctype, truncateKey) elif etype == Enctype.RC4: truncateKey = truncate(fullKey, 16) key = Key(cipher.enctype, truncateKey) cipherText = rep['enc-part']['cipher'].asOctets() plainText = cipher.decrypt(key, 3, cipherText) encASRepPart = decode(plainText, asn1Spec=EncASRepPart())[0] cipher = _enctype_table[int(encASRepPart['key']['keytype'])] session_key = Key(cipher.enctype, encASRepPart['key']['keyvalue'].asOctets()) return session_key, cipher, rep