Ejemplo n.º 1
0
 def _getCerts(self):
     for info in self._file.infolist():
         if info.filename.startswith(
                 'META-INF/') and info.filename.endswith('.RSA'):
             for cert in ContentInfo.load(
                     self._file.read(info))['content']['certificates']:
                 yield cert.dump()
Ejemplo n.º 2
0
def hash_receipt_body(receipt):

    # receipt_hash = hashlib.sha256(receipt.encode('utf-8')).hexdigest()

    # Load the contents of the receipt file
    # receipt_file = open('./receipt_data.bin', 'rb').read()
    logging.debug('hashing receipt')
    # receipt_file = bytearray.fromhex(receipt);
    # receipt_file = bytes.fromhex(receipt)
    receipt_file = base64.decodebytes(receipt.encode('utf-8'))

    # Use asn1crypto's cms definitions to parse the PKCS#7 format
    pkcs_container = ContentInfo.load(receipt_file)

    # Extract the certificates, signature, and receipt_data
    certificates = pkcs_container['content']['certificates']
    signer_info = pkcs_container['content']['signer_infos'][0]
    receipt_data = pkcs_container['content']['encap_content_info']['content']
    logging.debug(
        f'extracted certificates {len(str(certificates))}B signer_info {len(str(signer_info))}B '
        f'receipt_data {len(str(receipt_data))}B')

    receipt_data_str = str(receipt_data).encode('utf-8')[
        50:]  # slice the string to remove random header
    logging.debug(f'receipt_data_str: \n{receipt_data_str}')

    receipt_hash = hashlib.sha256(receipt_data_str).hexdigest()
    logging.debug(f'receipt_hash: {receipt_hash}')

    return receipt_hash
Ejemplo n.º 3
0
    def deserialize(self, s: BinaryIO):
        super().deserialize(s)
        assert self.magic
        assert self.length
        to_read = self.length - 8
        cms_data = sread(s, to_read)

        self.cms = ContentInfo.load(cms_data)
Ejemplo n.º 4
0
 def verify(self, pubkey):
     self.check_valid()
     with open(self.filepath, 'rb') as zipfile:
         zipfile.seek(0, os.SEEK_SET)
         message = zipfile.read(self.signed_len)
         zipfile.seek(-self.signature_start, os.SEEK_END)
         signature_size = self.signature_start - FOOTER_SIZE
         signature_raw = zipfile.read(signature_size)
     sig = ContentInfo.load(signature_raw)['content']['signer_infos'][0]
     sig_contents = sig['signature'].contents
     sig_type = DigestAlgorithmId.map(
         sig['digest_algorithm']['algorithm'].dotted)
     with open(pubkey, 'rb') as keyfile:
         keydata = load_public_key(keyfile.read())
     return rsa_pkcs1v15_verify(keydata, sig_contents, message, sig_type)
Ejemplo n.º 5
0
def decrypt_smime_content(payload: bytes, key: rsa.RSAPrivateKey) -> bytes:
    content_info = ContentInfo.load(payload)

    assert content_info['content_type'].native == 'enveloped_data'
    content: EnvelopedData = content_info['content']

    matching_recipient = content['recipient_infos'][0]

    # Need to see if we hold the key for any valid recipient.
    # for recipient_info in content['recipient_infos']:
    #     assert recipient_info.name == 'ktri'  # Only support KeyTransRecipientInfo
    #     ktri: KeyTransRecipientInfo = recipient_info.chosen
    #     recipient_id: RecipientIdentifier = ktri['rid']
    #     assert recipient_id.name == 'issuer_and_serial_number'  # Only support IssuerAndSerialNumber
    #     matching_recipient = recipient_info

    encryption_algo = matching_recipient.chosen['key_encryption_algorithm'].native
    encrypted_key = matching_recipient.chosen['encrypted_key'].native

    assert encryption_algo['algorithm'] == 'rsa'

    # Get the content key
    plain_key = key.decrypt(
        encrypted_key,
        padding=padding.PKCS1v15(),
    )

    # Now we have the plain key, we can decrypt the encrypted data
    encrypted_contentinfo = content['encrypted_content_info']

    algorithm: EncryptionAlgorithm = encrypted_contentinfo['content_encryption_algorithm']  #: EncryptionAlgorithm
    encrypted_content_bytes = encrypted_contentinfo['encrypted_content'].native

    symkey = None

    if algorithm.encryption_cipher == 'aes':
        symkey = AES(plain_key)
    elif algorithm.encryption_cipher == 'tripledes':
        symkey = TripleDES(plain_key)
    else:
        print('Dont understand encryption cipher: ', algorithm.encryption_cipher)

    cipher = Cipher(symkey, modes.CBC(algorithm.encryption_iv), backend=default_backend())
    decryptor = cipher.decryptor()

    decrypted_data = decryptor.update(encrypted_content_bytes) + decryptor.finalize()
    return decrypted_data
Ejemplo n.º 6
0
    def _pki_operation(self, identity, identity_private_key, envelope, message_type, cacaps, ca_certs, transaction_id=None):
        """Perform a PKIOperation using the PKI Envelope given."""
        envelope = envelope.add_recipient(ca_certs.recipient)

        envelope, key, iv = envelope.finalize()

        signer = Signer(identity, identity_private_key, cacaps.strongest_signature_algorithm())

        pki_msg_builder = PKIMessageBuilder().message_type(
            message_type
        ).pki_envelope(
            envelope
        ).add_signer(
            signer
        ).transaction_id(
            transaction_id
        ).sender_nonce()

        pki_msg = pki_msg_builder.finalize(digest_algorithm=cacaps.strongest_message_digest())

        res = self.__pki_operation(data=pki_msg.dump(), cacaps=cacaps)

        cert_rep = SCEPMessage.parse(raw=res.content, signer_cert=ca_certs.signer)
        cert_rep.debug()
        if cert_rep.pki_status == PKIStatus.FAILURE:
            return EnrollmentStatus(fail_info=cert_rep.fail_info)
        elif cert_rep.pki_status == PKIStatus.PENDING:
            return EnrollmentStatus(transaction_id=cert_rep.transaction_id)
        else:
            decrypted_bytes = cert_rep.get_decrypted_envelope_data(identity, identity_private_key)
            degenerate_info = ContentInfo.load(decrypted_bytes)
            assert degenerate_info['content_type'].native == 'signed_data'
            signed_response = degenerate_info['content']

            certificates = None
            revocation_list = None

            if (message_type is MessageType.PKCSReq) or (message_type is MessageType.GetCert) or (message_type is MessageType.CertPoll):
                certs = signed_response['certificates']
                certificates = [Certificate(der_string=cert.chosen.dump()) for cert in certs]
            elif message_type is MessageType.GetCRL:
                crls = signed_response['crls']
                received_crl = crls[0].chosen
                revocation_list = RevocationList(revocation_list=received_crl)

            return EnrollmentStatus(certificates=certificates, crl=revocation_list)
Ejemplo n.º 7
0
def getcacert(url: str) -> List[x509.Certificate]:
    """Query the SCEP Service for the CA Certificate."""
    res = requests.get(url, {'operation': 'GetCACert'})
    assert res.status_code == 200
    if res.headers[
            'content-type'] == 'application/x-x509-ca-cert':  # we dont support RA cert yet
        return [x509.load_der_x509_certificate(res.content, default_backend())]
    elif res.headers[
            'content-type'] == 'application/x-x509-ca-ra-cert':  # intermediate via chain
        ci = ContentInfo.load(res.content)
        #  print(ci['content_type'].native)
        assert ci['content_type'].native == 'signed_data'
        signed_data = ci['content']
        # convert certificates using cryptography lib since it is easier to deal with the decryption
        assert len(signed_data['certificates']) > 0
        certs = certificates_from_asn1(signed_data['certificates'])
        return certs
Ejemplo n.º 8
0
def find_recipient(cms_data: bytes) -> Optional[Certificate]:
    """Find the Certificate + Private Key of a recipient indicated by encoded CMS/PKCS#7 data from the database and
    return the database model that matches (if any).

    Requires that the indicated recipient is present in the `certificates` table, and has a matching private key in the
    `rsa_private_keys` table.
    """
    content_info = ContentInfo.load(cms_data)

    assert content_info['content_type'].native == 'enveloped_data'
    content: EnvelopedData = content_info['content']

    for recipient_info in content['recipient_infos']:
        if recipient_info.name == 'ktri':  # KeyTransRecipientInfo
            recipient: KeyTransRecipientInfo = recipient_info.chosen
            recipient_id: RecipientIdentifier = recipient['rid']
            assert recipient_id.name == 'issuer_and_serial_number'

        else:
            pass  # Unsupported recipient type

    return None
Ejemplo n.º 9
0
    def __init__(self, data: Union[str, bytes, BinaryIO]):
        """
        If data is a string, it is taken as a file name.

        If data is bytes, it is taken as p7m data.

        Otherwise, data is taken as a file-like object that reads bytes data.
        """
        if isinstance(data, str):
            with open(data, "rb") as fd:
                self.data = fd.read()
        elif isinstance(data, bytes):
            self.data = data
        else:
            self.data = data.read()

        # Data might potentially be base64 encoded

        try:
            self.data = base64.b64decode(self.data, validate=True)
        except binascii.Error:
            pass

        self.content_info = ContentInfo.load(self.data)
Ejemplo n.º 10
0
    def parse(cls, raw: bytes):
        msg = cls()

        cinfo = ContentInfo.load(raw)
        assert cinfo['content_type'].native == 'signed_data'
        signed_data = cinfo['content']

        # convert certificates from ASN.1 using cryptography lib since it is easier to deal with the decryption
        if len(signed_data['certificates']) > 0:
            certs = certificates_from_asn1(signed_data['certificates'])
            print('{} certificate(s) attached to signedData'.format(
                len(certs)))
            msg._certificates = certs
        else:
            certs = None
            print('No certificates attached to SignedData')

        # Iterate through signers and verify the signature for each.
        # Set convenience attributes at the same time
        for signer_info in cinfo['content']['signer_infos']:
            # version can be 1 (issuerandserial) or 3 (subjectkeyidentifier)
            # assert signer_info['version'] == 1
            identifier = signer_info['sid'].chosen
            assert isinstance(
                identifier,
                IssuerAndSerialNumber)  # TODO: also support other signer ids

            signer_cert = None
            if certs is not None:
                for c in certs:  # find signer cert
                    if c.serial_number == identifier[
                            'serial_number'].native:  # TODO: also convert issuer
                        signer_cert = c
                        break

            # assert signer_cert is not None

            sig_algo = signer_info['signature_algorithm'].signature_algo
            print('Using signature algorithm: {}'.format(sig_algo))
            hash_algo = signer_info['digest_algorithm']['algorithm'].native
            print('Using digest algorithm: {}'.format(hash_algo))

            if hash_algo == 'sha1':
                hasher = hashes.SHA1()
            elif hash_algo == 'sha256':
                hasher = hashes.SHA256()
            elif hash_algo == 'sha512':
                hasher = hashes.SHA512()
            else:
                raise ValueError(
                    'Unsupported hash algorithm: {}'.format(hash_algo))

            assert sig_algo == 'rsassa_pkcs1v15'  # We only support PKCS1v1.5
            if certs is not None and len(certs) > 0:  # verify content
                verifier = signer_cert.public_key().verifier(
                    signer_info['signature'].native, asympad.PKCS1v15(),
                    hasher)

                assert signed_data['encap_content_info'][
                    'content_type'].native == 'data'
                #verifier.update(signed_data['encap_content_info']['content'].native)
                if 'signed_attrs' in signer_info:
                    print('signed attrs added to signature')
                    verifier.update(signer_info['signed_attrs'].dump())

                # Calculate Digest
                content_digest = hashes.Hash(
                    hashes.SHA512(), backend=default_backend())  # Was: SHA-256
                content_digest.update(
                    signed_data['encap_content_info']['content'].native)
                content_digest_r = content_digest.finalize()
                # print('expecting SHA-256 digest: {}'.format(b64encode(content_digest_r)))
                for attr in signer_info['signed_attrs']:
                    if attr['type'].native == 'message_digest':
                        pass
                        # print('signer says digest is: {}'.format(b64encode(attr['values'][0].native)))

                # Calculate Digest on content + signed attrs
                cdsa = hashes.Hash(hashes.SHA512(),
                                   backend=default_backend())  # Was: SHA-256
                #cdsa.update(signed_data['encap_content_info']['content'].native)
                cdsa.update(signer_info['signed_attrs'].dump())
                cdsa_r = cdsa.finalize()
                # print('signature digest: {}'.format(b64encode(cdsa_r)))
                # print('expecting signature: {}'.format(b64encode(signer_info['signature'].native)))
                # verifier.verify()

            # Set the signer for convenience on the instance
            msg._signer_info = signer_info

            if 'signed_attrs' in signer_info:
                for signed_attr in signer_info['signed_attrs']:
                    name = asn1.SCEPCMSAttributeType.map(
                        signed_attr['type'].native)

                    if name == 'transaction_id':
                        msg._transaction_id = signed_attr['values'][0].native
                    elif name == 'message_type':
                        msg._message_type = MessageType(
                            signed_attr['values'][0].native)
                    elif name == 'sender_nonce':
                        msg._sender_nonce = signed_attr['values'][0].native
                    elif name == 'recipient_nonce':
                        msg._recipient_nonce = signed_attr['values'][0].native
                    elif name == 'pki_status':
                        msg._pki_status = signed_attr['values'][0].native
                    elif name == 'fail_info':
                        msg._fail_info = signed_attr['values'][0].native

        msg._signed_data = cinfo['content']['encap_content_info']['content']

        return msg
Ejemplo n.º 11
0
 def encap_content_info(self) -> ContentInfo:
     return ContentInfo.load(self._signed_data.native)
Ejemplo n.º 12
0
 def _getCerts(self):
  for info in self._file.infolist():
   if info.filename.startswith('META-INF/') and info.filename.endswith('.RSA'):
    for cert in ContentInfo.load(self._file.read(info))['content']['certificates']:
     yield cert.dump()
Ejemplo n.º 13
0
Archivo: debug.py Proyecto: mosen/SCEPy
import sys
import os.path
from asn1crypto.cms import SignedData, ContentInfo
from scepy import asn1
from asn1crypto.cms import CMSAttribute

CMSAttribute._fields = [
    ('type', asn1.SCEPCMSAttributeType),
    ('values', None),
]

if __name__ == '__main__':
    if len(sys.argv) != 2:
        sys.exit(1)

    with open(sys.argv[1], 'rb') as fd:
        content = ContentInfo.load(fd.read())
        content.debug()
Ejemplo n.º 14
0
def extract_certificates(binary, data):
    """
    Extract and parse certificates from the file
    Largely inspired from macholibre
    """
    header = b"\xfa\xde\x0c\x05"
    cdata = None
    res = {}
    for c in binary.commands:
        if c.command.name == "CODE_SIGNATURE":
            cdata = data[c.data_offset:c.data_offset + c.data_size]
    if not cdata:
        return {}
    if not cdata.startswith(b'\xfa\xde\x0c\xc0'):
        print("Invalid Code signature header, weird")
        return {}
    b = BytesIO(cdata)
    _ = b.read(4)  # Magic number
    size = read_int(b)
    count = read_int(b)

    for _ in range(count):
        index_type = read_int(b)
        if index_type not in CODE_INDEX:
            print("Unknown code signature index")
            b.read(4)
            continue
        index_offset = read_int(b)
        if index_type == 0x10000:
            # Certificates
            bb = BytesIO(cdata[index_offset:])
            if bb.read(4) != b"\xfa\xde\x0b\x01":
                print("Unknown magic type for certificates")
                continue
            size = read_int(bb)
            signed_data = ContentInfo.load(bb.read(size))['content']
            res['certs'] = []
            for cert in signed_data['certificates']:
                cert = cert.chosen
                subject = {}
                for rdn in cert.subject.chosen:
                    name = rdn[0]['type'].human_friendly
                    value = rdn[0]['value']

                    if name == 'Country':
                        subject['country'] = str(value.chosen)
                    elif name == 'Organization':
                        subject['org'] = str(value.chosen)
                    elif name == 'Organizational Unit':
                        subject['org_unit'] = str(value.chosen)
                    elif name == 'Common Name':
                        subject['common_name'] = str(value.chosen)
                    else:
                        if isinstance(value, DirectoryString):
                            subject[name] = str(value.chosen)
                        else:
                            subject[name] = str(value.parsed)

                issuer = {}

                for rdn in cert.issuer.chosen:
                    name = rdn[0]['type'].human_friendly
                    value = rdn[0]['value']

                    if name == 'Country':
                        issuer['country'] = str(value.chosen)
                    elif name == 'Organization':
                        issuer['org'] = str(value.chosen)
                    elif name == 'Organizational Unit':
                        issuer['org_unit'] = str(value.chosen)
                    elif name == 'Common Name':
                        issuer['common_name'] = str(value.chosen)
                    else:
                        if isinstance(value, DirectoryString):
                            issuer[name] = str(value.chosen)
                        else:
                            issuer[name] = str(value.parsed)

                res['certs'].append({
                    'subject': subject,
                    'issuer': issuer,
                    'serial': cert.serial_number,
                    'is_ca': cert.ca
                })

    return res
Ejemplo n.º 15
0
def pkcsreq(url: str, private_key_path: str = None):
    """Perform a PKCSReq operation by submitting a CSR to the SCEP service."""

    logger.info('Request: GetCACaps')
    cacaps = getcacaps(url)
    logger.debug(cacaps)
    logger.info('Request: GetCACert')
    ca_certificates = getcacert(url)
    logger.debug('CA Certificate Subject(s) Follows')
    for c in ca_certificates:
        logger.debug(c.subject)

    if private_key_path:
        with open(private_key_path, 'rb') as fd:
            data = fd.read()
            private_key = serialization.load_pem_private_key(
                data, backend=default_backend(), password=None)

        logger.debug('Successfully read private key from filesystem')
        private_key, csr = generate_csr(private_key)
    else:
        private_key, csr = generate_csr()

        logger.debug('Writing RSA private key to ./scep.key')
        pem = private_key.private_bytes(
            encoding=serialization.Encoding.PEM,
            format=serialization.PrivateFormat.PKCS8,
            encryption_algorithm=serialization.NoEncryption(),
        )
        with open('scep.key', 'wb') as fd:
            fd.write(pem)

    ssc = generate_self_signed(
        private_key,
        x509.Name([
            x509.NameAttribute(NameOID.COMMON_NAME, 'SCEPy SCEP SIGNER'),
            x509.NameAttribute(NameOID.COUNTRY_NAME, 'US'),
        ]))

    envelope = PKCSPKIEnvelopeBuilder().encrypt(
        csr.public_bytes(serialization.Encoding.DER), '3des')
    # Temporary for MSCEP/NDES
    #envelope.add_recipient(ca_certificates[1])

    for recipient in ca_certificates:
        envelope = envelope.add_recipient(recipient)

    envelope, key, iv = envelope.finalize()

    with open('scepyclient.csr', 'wb') as fd:
        fd.write(csr.public_bytes(serialization.Encoding.DER))

    signer = Signer(ssc, private_key, 'sha512')

    pki_msg_builder = PKIMessageBuilder().message_type(
        MessageType.PKCSReq).pki_envelope(envelope).add_signer(
            signer).transaction_id().sender_nonce()

    pki_msg = pki_msg_builder.finalize()

    # if args.dump_request:
    #     with open(args.dump_pkcsreq, 'wb') as fd:
    #         fd.write(pki_msg.dump())
    #     logger.debug('Dumped PKCSReq data to {}'.format(args.dump_pkcsreq))

    with open('scepyclient-request.bin', 'wb') as fd:
        fd.write(pki_msg.dump())

    res = pkioperation(url, data=pki_msg.dump())

    logger.debug('Response: Status {}'.format(res.status_code))
    if res.status_code != 200:
        return -1
    #
    # with open('ndes-response.bin', 'wb') as fd:

    cert_rep = SCEPMessage.parse(res.content)
    # if args.dump_response:
    #     with open(args.dump_response, 'wb') as fd:
    #         fd.write(res.content)
    #     logger.debug('Dumped CertRep data to {}'.format(args.dump_response))

    logger.debug('pkiMessage response follows')
    logger.debug('Transaction ID: %s', cert_rep.transaction_id)
    logger.debug('PKI Status: %s', PKIStatus(cert_rep.pki_status))

    if PKIStatus(cert_rep.pki_status) == PKIStatus.FAILURE:
        logger.error('SCEP Request Failed: {}'.format(
            FailInfo(cert_rep.fail_info)))

    elif PKIStatus(cert_rep.pki_status) == PKIStatus.SUCCESS:
        # This should be the PKCS#7 Degenerate
        decrypted_bytes = cert_rep.get_decrypted_envelope_data(
            ssc, private_key)
        degenerate_info = ContentInfo.load(decrypted_bytes)
        degenerate_info.debug()

        assert degenerate_info['content_type'].native == 'signed_data'
        signed_response = degenerate_info['content']
        certs = signed_response['certificates']

        my_cert = certs[0].chosen

        result = x509.load_der_x509_certificate(my_cert.dump(),
                                                default_backend())
        subject = result.subject

        logger.info(
            'SCEP CA issued a certificate with serial #{}, subject: {}'.format(
                result.serial_number, subject))

        pem_data = result.public_bytes(serialization.Encoding.PEM)
        with open('scep.cer', 'wb') as fd:
            fd.write(pem_data)
Ejemplo n.º 16
0
    def parse_certs(self, signature, offset):
        prev = self.f.tell()
        true_offset = signature.offset + offset
        self.f.seek(true_offset)
        magic = get_int(self.f)
        if magic != dictionary.signatures['BLOBWRAPPER']:
            data = {
                'offset': true_offset,
                'magic': hex(magic),
                'expected': hex(dictionary.signatures['BLOBWRAPPER'])
            }
            a = Abnormality(title='BAD MAGIC - BLOBWRAPPER', data=data)
            self.add_abnormality(a)
            self.f.seek(prev)
            return

        size = get_int(self.f) - 8

        if size > 0:
            signed_data = ContentInfo.load(self.f.read(size))['content']

            for cert in signed_data['certificates']:
                cert = cert.chosen

                serial = cert.serial_number

                subject = {}

                for rdn in cert.subject.chosen:
                    name = rdn[0]['type'].human_friendly
                    value = unicode(rdn[0]['value'].chosen)

                    if name == 'Country':
                        subject['country'] = value
                    elif name == 'Organization':
                        subject['org'] = value
                    elif name == 'Organizational Unit':
                        subject['org_unit'] = value
                    elif name == 'Common Name':
                        subject['common_name'] = value

                issuer = {}

                for rdn in cert.issuer.chosen:
                    name = rdn[0]['type'].human_friendly
                    value = unicode(rdn[0]['value'].chosen)

                    if name == 'Country':
                        issuer['country'] = value
                    elif name == 'Organization':
                        issuer['org'] = value
                    elif name == 'Organizational Unit':
                        issuer['org_unit'] = value
                    elif name == 'Common Name':
                        issuer['common_name'] = value

                is_ca = cert.ca

                cert = Certificate(serial=serial,
                                   subject=subject,
                                   issuer=issuer,
                                   ca=is_ca)

                signature.add_cert(cert)
        else:
            data = {'offset': true_offset, 'size': size}
            a = Abnormality(title='NON-POSITIVE CMS SIZE', data=data)
            self.add_abnormality(a)

        self.f.seek(prev)