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()
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
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)
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)
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
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)
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
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
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)
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
def encap_content_info(self) -> ContentInfo: return ContentInfo.load(self._signed_data.native)
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()
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()
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
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)
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)