def __init__( self, server_info: ServerConnectivityInfo, scan_command: HttpHeadersScanCommand, raw_hsts_header: Optional[str], raw_hpkp_header: Optional[str], raw_expect_ct_header: Optional[str], hpkp_report_only: bool, cert_chain: List[Certificate], ) -> None: super().__init__(server_info, scan_command) self.hsts_header = ParsedHstsHeader(raw_hsts_header) if raw_hsts_header else None self.hpkp_header = ParsedHpkpHeader(raw_hpkp_header, hpkp_report_only) if raw_hpkp_header else None self.expect_ct_header = ParsedExpectCTHeader(raw_expect_ct_header) if raw_expect_ct_header else None self.verified_certificate_chain: List[Certificate] = [] try: main_trust_store = TrustStoresRepository.get_default().get_main_store() self.verified_certificate_chain = main_trust_store.build_verified_certificate_chain(cert_chain) except CouldNotBuildVerifiedChainError: pass # Is the pinning configuration valid? self.is_valid_pin_configured = None self.is_backup_pin_configured = None if self.verified_certificate_chain and self.hpkp_header: # Is one of the configured pins in the current server chain? self.is_valid_pin_configured = False server_pin_list = [CertificateUtils.get_hpkp_pin(cert) for cert in self.verified_certificate_chain] for pin in self.hpkp_header.pin_sha256_list: if pin in server_pin_list: self.is_valid_pin_configured = True break # Is a backup pin configured? self.is_backup_pin_configured = set(self.hpkp_header.pin_sha256_list) != set(server_pin_list)
def as_text(self): txt_result = [self._format_title(self.scan_command.get_title())] if self.hsts_header: txt_result.append(self._format_subtitle('HTTP Strict Transport Security (HSTS)')) txt_result.append(self._format_field("Max Age:", str(self.hsts_header.max_age))) txt_result.append(self._format_field("Include Subdomains:", str(self.hsts_header.include_subdomains))) txt_result.append(self._format_field("Preload:", str(self.hsts_header.preload))) else: txt_result.append(self._format_field("NOT SUPPORTED - Server did not send an HSTS header", "")) computed_hpkp_pins_text = ['', self._format_subtitle('Computed HPKP Pins for Current Chain')] if self.verified_certificate_chain: for index, cert in enumerate(self.verified_certificate_chain, start=0): final_subject = CertificateUtils.get_name_as_short_text(cert.subject) if len(final_subject) > 40: # Make the CN shorter when displaying it final_subject = '{}...'.format(final_subject[:40]) computed_hpkp_pins_text.append( self.PIN_TXT_FORMAT(('{} - {}'.format(index, final_subject)), CertificateUtils.get_hpkp_pin(cert)) ) else: computed_hpkp_pins_text.append( self._format_field('ERROR - Could not build verified chain (certificate untrusted?)', '') ) txt_result.extend(['', self._format_subtitle('HTTP Public Key Pinning (HPKP)')]) if self.hpkp_header: txt_result.append(self._format_field("Max Age:", str(self.hpkp_header.max_age))) txt_result.append(self._format_field("Include Subdomains:", str(self.hpkp_header.include_subdomains))) txt_result.append(self._format_field("Report URI:", self.hpkp_header.report_uri)) txt_result.append(self._format_field("Report Only:", str(self.hpkp_header.report_only))) txt_result.append(self._format_field("SHA-256 Pin List:", ', '.join(self.hpkp_header.pin_sha256_list))) if self.verified_certificate_chain: pin_validation_txt = 'OK - One of the configured pins was found in the certificate chain' \ if self.is_valid_pin_configured \ else 'FAILED - Could NOT find any of the configured pins in the certificate chain!' txt_result.append(self._format_field("Valid Pin:", pin_validation_txt)) backup_txt = 'OK - Backup pin found in the configured pins' \ if self.is_backup_pin_configured \ else 'FAILED - No backup pin found: all the configured pins are in the certificate chain!' txt_result.append(self._format_field("Backup Pin:", backup_txt)) else: txt_result.append(self._format_field("NOT SUPPORTED - Server did not send an HPKP header", "")) # Dislpay computed HPKP pins txt_result.extend(computed_hpkp_pins_text) txt_result.extend(['', self._format_subtitle('HTTP Expect-CT')]) if self.expect_ct_header: txt_result.append(self._format_field('Max Age:', str(self.expect_ct_header.max_age))) txt_result.append(self._format_field('Report- URI:', self.expect_ct_header.report_uri)) txt_result.append(self._format_field('Enforce:', str(self.expect_ct_header.enforce))) else: txt_result.append(self._format_field("NOT SUPPORTED - Server did not send an Expect-CT header", "")) return txt_result
def _certificate_chain_to_xml(certificate_chain: List[Certificate]) -> List[Element]: cert_xml_list = [] for certificate in certificate_chain: cert_xml = Element('certificate', attrib={ 'sha1Fingerprint': binascii.hexlify(certificate.fingerprint(hashes.SHA1())).decode('ascii'), 'hpkpSha256Pin': CertificateUtils.get_hpkp_pin(certificate) }) # Add the PEM cert cert_as_pem_xml = Element('asPEM') cert_as_pem_xml.text = certificate.public_bytes(Encoding.PEM).decode('ascii') cert_xml.append(cert_as_pem_xml) # Add some of the fields of the cert elem_xml = Element('subject') elem_xml.text = CertificateUtils.get_name_as_text(certificate.subject) cert_xml.append(elem_xml) elem_xml = Element('issuer') elem_xml.text = CertificateUtils.get_name_as_text(certificate.issuer) cert_xml.append(elem_xml) elem_xml = Element('serialNumber') elem_xml.text = str(certificate.serial_number) cert_xml.append(elem_xml) elem_xml = Element('notBefore') elem_xml.text = certificate.not_valid_before.strftime("%Y-%m-%d %H:%M:%S") cert_xml.append(elem_xml) elem_xml = Element('notAfter') elem_xml.text = certificate.not_valid_after.strftime("%Y-%m-%d %H:%M:%S") cert_xml.append(elem_xml) elem_xml = Element('signatureAlgorithm') elem_xml.text = certificate.signature_hash_algorithm.name cert_xml.append(elem_xml) key_attrs = {'algorithm': CertificateUtils.get_public_key_type(certificate)} public_key = certificate.public_key() key_attrs['size'] = str(public_key.key_size) if isinstance(public_key, EllipticCurvePublicKey): key_attrs['curve'] = public_key.curve.name else: key_attrs['exponent'] = str(public_key.public_numbers().e) elem_xml = Element('publicKey', attrib=key_attrs) cert_xml.append(elem_xml) dns_alt_names = CertificateUtils.get_dns_subject_alternative_names(certificate) if dns_alt_names: san_xml = Element('subjectAlternativeName') for dns_name in dns_alt_names: dns_xml = Element('DNS') dns_xml.text = dns_name san_xml.append(dns_xml) cert_xml.append(san_xml) cert_xml_list.append(cert_xml) return cert_xml_list
def _object_to_json_dict(obj): """Convert an object to a dictionary suitable for the JSON output. """ if isinstance(obj, Enum): # Properly serialize Enums (such as OpenSslVersionEnum) result = obj.name elif isinstance(obj, x509._Certificate): # Properly serialize certificates; only return the PEM string result = { 'as_pem': obj.public_bytes(Encoding.PEM).decode('ascii'), 'hpkp_pin': CertificateUtils.get_hpkp_pin(obj), 'subject_name': CertificateUtils.get_name_as_short_text(obj.subject) } elif isinstance(obj, object): result = {} for key, value in obj.__dict__.items(): # Remove private attributes if key.startswith('_'): continue result[key] = value else: raise TypeError('Unknown type: {}'.format(repr(obj))) return result
def __init__( self, server_info, # type: ServerConnectivityInfo scan_command, # type: HttpHeadersScanCommand raw_hsts_header, # type: Text raw_hpkp_header, # type: Text hpkp_report_only, # type: bool cert_chain # type: List[cryptography.x509.Certificate] ): # type: (...) -> None super(HttpHeadersScanResult, self).__init__(server_info, scan_command) self.hsts_header = ParsedHstsHeader(raw_hsts_header) if raw_hsts_header else None self.hpkp_header = ParsedHpkpHeader(raw_hpkp_header, hpkp_report_only) if raw_hpkp_header else None self.verified_certificate_chain = [] try: main_trust_store = TrustStoresRepository.get_default().get_main_store() self.verified_certificate_chain = main_trust_store.build_verified_certificate_chain(cert_chain) except CouldNotBuildVerifiedChainError: pass # Is the pinning configuration valid? self.is_valid_pin_configured = None self.is_backup_pin_configured = None if self.verified_certificate_chain and self.hpkp_header: # Is one of the configured pins in the current server chain? self.is_valid_pin_configured = False server_pin_list = [CertificateUtils.get_hpkp_pin(cert) for cert in self.verified_certificate_chain] for pin in self.hpkp_header.pin_sha256_list: if pin in server_pin_list: self.is_valid_pin_configured = True break # Is a backup pin configured? self.is_backup_pin_configured = set(self.hpkp_header.pin_sha256_list) != set(server_pin_list)
def as_text(self) -> List[str]: txt_result = [self._format_title(self.scan_command.get_title())] if self.hsts_header: txt_result.append(self._format_subtitle('HTTP Strict Transport Security (HSTS)')) txt_result.append(self._format_field("Max Age:", str(self.hsts_header.max_age))) txt_result.append(self._format_field("Include Subdomains:", str(self.hsts_header.include_subdomains))) txt_result.append(self._format_field("Preload:", str(self.hsts_header.preload))) else: txt_result.append(self._format_field("NOT SUPPORTED - Server did not send an HSTS header", "")) computed_hpkp_pins_text = ['', self._format_subtitle('Computed HPKP Pins for Current Chain')] if self.verified_certificate_chain: for index, cert in enumerate(self.verified_certificate_chain, start=0): final_subject = CertificateUtils.get_name_as_short_text(cert.subject) if len(final_subject) > 40: # Make the CN shorter when displaying it final_subject = '{}...'.format(final_subject[:40]) computed_hpkp_pins_text.append( self.PIN_TXT_FORMAT(('{} - {}'.format(index, final_subject)), CertificateUtils.get_hpkp_pin(cert)) ) else: computed_hpkp_pins_text.append( self._format_field('ERROR - Could not build verified chain (certificate untrusted?)', '') ) txt_result.extend(['', self._format_subtitle('HTTP Public Key Pinning (HPKP)')]) if self.hpkp_header: txt_result.append(self._format_field("Max Age:", str(self.hpkp_header.max_age))) txt_result.append(self._format_field("Include Subdomains:", str(self.hpkp_header.include_subdomains))) txt_result.append(self._format_field("Report URI:", str(self.hpkp_header.report_uri))) txt_result.append(self._format_field("Report Only:", str(self.hpkp_header.report_only))) txt_result.append(self._format_field("SHA-256 Pin List:", ', '.join(self.hpkp_header.pin_sha256_list))) if self.verified_certificate_chain: pin_validation_txt = 'OK - One of the configured pins was found in the certificate chain' \ if self.is_valid_pin_configured \ else 'FAILED - Could NOT find any of the configured pins in the certificate chain!' txt_result.append(self._format_field("Valid Pin:", pin_validation_txt)) backup_txt = 'OK - Backup pin found in the configured pins' \ if self.is_backup_pin_configured \ else 'FAILED - No backup pin found: all the configured pins are in the certificate chain!' txt_result.append(self._format_field("Backup Pin:", backup_txt)) else: txt_result.append(self._format_field("NOT SUPPORTED - Server did not send an HPKP header", "")) # Dislpay computed HPKP pins txt_result.extend(computed_hpkp_pins_text) txt_result.extend(['', self._format_subtitle('HTTP Expect-CT')]) if self.expect_ct_header: txt_result.append(self._format_field('Max Age:', str(self.expect_ct_header.max_age))) txt_result.append(self._format_field('Report- URI:', str(self.expect_ct_header.report_uri))) txt_result.append(self._format_field('Enforce:', str(self.expect_ct_header.enforce))) else: txt_result.append(self._format_field("NOT SUPPORTED - Server did not send an Expect-CT header", "")) return txt_result
def _object_to_json_dict(obj): """Convert an object to a dictionary suitable for the JSON output. """ if isinstance(obj, Enum): # Properly serialize Enums (such as OpenSslVersionEnum) result = obj.name elif isinstance(obj, x509._Certificate): # Properly serialize certificates certificate = obj result = { # Add general info 'as_pem': obj.public_bytes(Encoding.PEM).decode('ascii'), 'hpkp_pin': CertificateUtils.get_hpkp_pin(obj), # Add some of the fields of the cert 'subject': CertificateUtils.get_name_as_text(certificate.subject), 'issuer': CertificateUtils.get_name_as_text(certificate.issuer), 'serialNumber': str(certificate.serial_number), 'notBefore': certificate.not_valid_before.strftime("%Y-%m-%d %H:%M:%S"), 'notAfter': certificate.not_valid_after.strftime("%Y-%m-%d %H:%M:%S"), 'signatureAlgorithm': certificate.signature_hash_algorithm.name, 'publicKey': { 'algorithm': CertificateUtils.get_public_key_type(certificate) }, } dns_alt_names = CertificateUtils.get_dns_subject_alternative_names( certificate) if dns_alt_names: result['subjectAlternativeName'] = {'DNS': dns_alt_names} # Add some info about the public key public_key = certificate.public_key() if isinstance(public_key, EllipticCurvePublicKey): result['publicKey']['size'] = str(public_key.curve.key_size) result['publicKey']['curve'] = public_key.curve.name else: result['publicKey']['size'] = str(public_key.key_size) result['publicKey']['exponent'] = str( public_key.public_numbers().e) elif isinstance(obj, object): if hasattr(obj, '__dict__'): result = {} for key, value in obj.__dict__.items(): # Remove private attributes if key.startswith('_'): continue result[key] = _object_to_json_dict(value) else: # Simple object like a string result = obj else: raise TypeError('Unknown type: {}'.format(repr(obj))) return result
def _object_to_json_dict(obj: Any) -> Union[bool, int, float, str, Dict[str, Any]]: """Convert an object to a dictionary suitable for the JSON output. """ if isinstance(obj, Enum): # Properly serialize Enums (such as OpenSslVersionEnum) result = obj.name elif isinstance(obj, x509._Certificate): # Properly serialize certificates certificate = obj result = { # type: ignore # Add general info 'as_pem': obj.public_bytes(Encoding.PEM).decode('ascii'), 'hpkp_pin': CertificateUtils.get_hpkp_pin(obj), # Add some of the fields of the cert 'subject': CertificateUtils.get_name_as_text(certificate.subject), 'issuer': CertificateUtils.get_name_as_text(certificate.issuer), 'serialNumber': str(certificate.serial_number), 'notBefore': certificate.not_valid_before.strftime("%Y-%m-%d %H:%M:%S"), 'notAfter': certificate.not_valid_after.strftime("%Y-%m-%d %H:%M:%S"), 'signatureAlgorithm': certificate.signature_hash_algorithm.name, 'publicKey': { 'algorithm': CertificateUtils.get_public_key_type(certificate) }, } dns_alt_names = CertificateUtils.get_dns_subject_alternative_names(certificate) if dns_alt_names: result['subjectAlternativeName'] = {'DNS': dns_alt_names} # type: ignore # Add some info about the public key public_key = certificate.public_key() if isinstance(public_key, EllipticCurvePublicKey): result['publicKey']['size'] = str(public_key.curve.key_size) # type: ignore result['publicKey']['curve'] = public_key.curve.name # type: ignore else: result['publicKey']['size'] = str(public_key.key_size) result['publicKey']['exponent'] = str(public_key.public_numbers().e) elif isinstance(obj, object): # Some objects (like str) don't have a __dict__ if hasattr(obj, '__dict__'): result = {} for key, value in obj.__dict__.items(): # Remove private attributes if key.startswith('_'): continue result[key] = _object_to_json_dict(value) else: # Simple object like a bool result = obj else: raise TypeError('Unknown type: {}'.format(repr(obj))) return result
def __init__( self, server_info: ServerConnectivityInfo, scan_command: HttpHeadersScanCommand, raw_hsts_header: Optional[str], raw_hpkp_header: Optional[str], raw_expect_ct_header: Optional[str], hpkp_report_only: bool, cert_chain: List[Certificate], ) -> None: super().__init__(server_info, scan_command) self.hsts_header = ParsedHstsHeader( raw_hsts_header) if raw_hsts_header else None self.hpkp_header = ParsedHpkpHeader( raw_hpkp_header, hpkp_report_only) if raw_hpkp_header else None self.expect_ct_header = ParsedExpectCtHeader( raw_expect_ct_header) if raw_expect_ct_header else None self.verified_certificate_chain: List[Certificate] = [] try: main_trust_store = TrustStoresRepository.get_default( ).get_main_store() self.verified_certificate_chain = main_trust_store.build_verified_certificate_chain( cert_chain) except CouldNotBuildVerifiedChainError: pass # Is the pinning configuration valid? self.is_valid_pin_configured = None self.is_backup_pin_configured = None if self.verified_certificate_chain and self.hpkp_header: # Is one of the configured pins in the current server chain? self.is_valid_pin_configured = False server_pin_list = [ CertificateUtils.get_hpkp_pin(cert) for cert in self.verified_certificate_chain ] for pin in self.hpkp_header.pin_sha256_list: if pin in server_pin_list: self.is_valid_pin_configured = True break # Is a backup pin configured? self.is_backup_pin_configured = set( self.hpkp_header.pin_sha256_list) != set(server_pin_list)
def __init__( self, server_info: ServerConnectivityInfo, scan_command: HttpHeadersScanCommand, strict_transport_security_header: Optional[ StrictTransportSecurityHeader], public_key_pins_header: Optional[PublicKeyPinsHeader], public_key_pins_report_only_header: Optional[ PublicKeyPinsReportOnlyHeader], expect_ct_header: Optional[ExpectCtHeader], verified_chain: Optional[List[Certificate]], ) -> None: super().__init__(server_info, scan_command) self.strict_transport_security_header = strict_transport_security_header self.public_key_pins_header = public_key_pins_header self.public_key_pins_report_only_header = public_key_pins_report_only_header self.expect_ct_header = expect_ct_header self.verified_certificate_chain = verified_chain # Is the pinning configuration valid? self.is_valid_pin_configured = None self.is_backup_pin_configured = None returned_hpkp_header = None if self.public_key_pins_header: returned_hpkp_header = self.public_key_pins_header elif self.public_key_pins_report_only_header: returned_hpkp_header = self.public_key_pins_report_only_header if self.verified_certificate_chain and returned_hpkp_header: # Is one of the configured pins in the current server chain? self.is_valid_pin_configured = False server_pin_list = [ CertificateUtils.get_hpkp_pin(cert) for cert in self.verified_certificate_chain ] for pin in returned_hpkp_header.pin_sha256_list: if pin in server_pin_list: self.is_valid_pin_configured = True break # Is a backup pin configured? self.is_backup_pin_configured = set( returned_hpkp_header.pin_sha256_list) != set(server_pin_list)
def _certificate_chain_to_xml(certificate_chain): # type: (List[cryptography.x509.Certificate]) -> List[Element] cert_xml_list = [] for certificate in certificate_chain: cert_xml = Element('certificate', attrib={ 'sha1Fingerprint': binascii.hexlify( certificate.fingerprint( hashes.SHA1())).decode('ascii'), 'hpkpSha256Pin': CertificateUtils.get_hpkp_pin(certificate) }) # Add the PEM cert cert_as_pem_xml = Element('asPEM') cert_as_pem_xml.text = certificate.public_bytes(Encoding.PEM) cert_xml.append(cert_as_pem_xml) cert_xml_list.append(cert_xml) return cert_xml_list
def __init__( self, server_info: ServerConnectivityInfo, scan_command: HttpHeadersScanCommand, strict_transport_security_header: Optional[StrictTransportSecurityHeader], public_key_pins_header: Optional[PublicKeyPinsHeader], public_key_pins_report_only_header: Optional[PublicKeyPinsReportOnlyHeader], expect_ct_header: Optional[ExpectCtHeader], verified_chain: Optional[List[Certificate]], ) -> None: super().__init__(server_info, scan_command) self.strict_transport_security_header = strict_transport_security_header self.public_key_pins_header = public_key_pins_header self.public_key_pins_report_only_header = public_key_pins_report_only_header self.expect_ct_header = expect_ct_header self.verified_certificate_chain = verified_chain # Is the pinning configuration valid? self.is_valid_pin_configured = None self.is_backup_pin_configured = None returned_hpkp_header = None if self.public_key_pins_header: returned_hpkp_header = self.public_key_pins_header elif self.public_key_pins_report_only_header: returned_hpkp_header = self.public_key_pins_report_only_header if self.verified_certificate_chain and returned_hpkp_header: # Is one of the configured pins in the current server chain? self.is_valid_pin_configured = False server_pin_list = [CertificateUtils.get_hpkp_pin(cert) for cert in self.verified_certificate_chain] for pin in returned_hpkp_header.pin_sha256_list: if pin in server_pin_list: self.is_valid_pin_configured = True break # Is a backup pin configured? self.is_backup_pin_configured = set(returned_hpkp_header.pin_sha256_list) != set(server_pin_list)
def default(self, obj: Any) -> Union[bool, int, float, str, Dict[str, Any]]: result: Union[bool, int, float, str, Dict[str, Any]] if isinstance(obj, Enum): result = obj.name elif isinstance(obj, ObjectIdentifier): result = obj.dotted_string elif isinstance(obj, x509._Certificate): certificate = obj result = { # Add general info "as_pem": obj.public_bytes(Encoding.PEM).decode("ascii"), "hpkp_pin": CertificateUtils.get_hpkp_pin(obj), # Add some of the fields of the cert "subject": CertificateUtils.get_name_as_text(certificate.subject), "issuer": CertificateUtils.get_name_as_text(certificate.issuer), "serialNumber": str(certificate.serial_number), "notBefore": certificate.not_valid_before.strftime("%Y-%m-%d %H:%M:%S"), "notAfter": certificate.not_valid_after.strftime("%Y-%m-%d %H:%M:%S"), "signatureAlgorithm": certificate.signature_hash_algorithm.name, "publicKey": { "algorithm": CertificateUtils.get_public_key_type(certificate) }, } dns_alt_names = CertificateUtils.get_dns_subject_alternative_names( certificate) if dns_alt_names: result["subjectAlternativeName"] = { "DNS": dns_alt_names } # type: ignore # Add some info about the public key public_key = certificate.public_key() if isinstance(public_key, EllipticCurvePublicKey): result["publicKey"]["size"] = str( public_key.curve.key_size) # type: ignore result["publicKey"][ "curve"] = public_key.curve.name # type: ignore else: result["publicKey"]["size"] = str(public_key.key_size) result["publicKey"]["exponent"] = str( public_key.public_numbers().e) elif isinstance(obj, Path): result = str(obj) elif isinstance(obj, object): # Some objects (like str) don't have a __dict__ if hasattr(obj, "__dict__"): result = {} for key, value in obj.__dict__.items(): # Remove private attributes if key.startswith("_"): continue result[key] = self.default(value) else: # Simple object like a bool result = obj # type: ignore else: raise TypeError("Unknown type: {}".format(repr(obj))) return result
def as_xml(self): xml_output = Element(self.scan_command.get_cli_argument(), title=self.COMMAND_TITLE) # Certificate chain cert_xml_list = [] for index, certificate in enumerate(self.certificate_chain, start=0): cert_xml = Element('certificate', attrib={ 'sha1Fingerprint': binascii.hexlify( certificate.fingerprint( hashes.SHA1())).decode('ascii'), 'position': 'leaf' if index == 0 else 'intermediate', 'suppliedServerNameIndication': self.server_info.tls_server_name_indication, 'hpkpSha256Pin': CertificateUtils.get_hpkp_pin(certificate) }) # Add the PEM cert cert_as_pem_xml = Element('asPEM') cert_as_pem_xml.text = certificate.public_bytes(Encoding.PEM) cert_xml.append(cert_as_pem_xml) cert_chain_attrs = { 'isChainOrderValid': str(self.is_certificate_chain_order_valid) } if self.verified_certificate_chain: cert_chain_attrs['containsAnchorCertificate'] = str(False) if not self.has_anchor_in_certificate_chain \ else str(True) cert_chain_xml = Element('receivedCertificateChain', attrib=cert_chain_attrs) for cert_xml in cert_xml_list: cert_chain_xml.append(cert_xml) xml_output.append(cert_chain_xml) # Trust trust_validation_xml = Element('certificateValidation') # Hostname validation host_validation_xml = Element( 'hostnameValidation', serverHostname=self.server_info.tls_server_name_indication, certificateMatchesServerHostname=str( self.certificate_matches_hostname)) trust_validation_xml.append(host_validation_xml) # Path validation that was successful for path_result in self.path_validation_result_list: path_attrib_xml = { 'usingTrustStore': path_result.trust_store.name, 'trustStoreVersion': path_result.trust_store.version, 'validationResult': path_result.verify_string } # Things we only do with the Mozilla store verified_cert_chain_xml = None if 'Mozilla' in path_result.trust_store.name: # EV certs if self.is_leaf_certificate_ev: path_attrib_xml['isExtendedValidationCertificate'] = str( self.is_leaf_certificate_ev) # Verified chain if self.verified_certificate_chain: verified_cert_chain_xml = Element( 'verifiedCertificateChain', { 'hasSha1SignedCertificate': str(self.has_sha1_in_certificate_chain) }) for certificate in self.certificate_chain: cert_xml = Element( 'certificate', attrib={ 'sha1Fingerprint': binascii.hexlify( certificate.fingerprint( hashes.SHA1())).decode('ascii'), 'suppliedServerNameIndication': self.server_info.tls_server_name_indication, 'hpkpSha256Pin': CertificateUtils.get_hpkp_pin(certificate) }) # Add the PEM cert cert_as_pem_xml = Element('asPEM') cert_as_pem_xml.text = certificate.public_bytes( Encoding.PEM) cert_xml.append(cert_as_pem_xml) verified_cert_chain_xml.append(cert_xml) path_valid_xml = Element('pathValidation', attrib=path_attrib_xml) if verified_cert_chain_xml is not None: path_valid_xml.append(verified_cert_chain_xml) trust_validation_xml.append(path_valid_xml) # Path validation that ran into errors for path_error in self.path_validation_error_list: error_txt = 'ERROR: {}'.format(path_error.error_message) path_attrib_xml = { 'usingTrustStore': path_result.trust_store.name, 'trustStoreVersion': path_result.trust_store.version, 'error': error_txt } trust_validation_xml.append( Element('pathValidation', attrib=path_attrib_xml)) xml_output.append(trust_validation_xml) # OCSP Stapling ocsp_xml = Element( 'ocspStapling', attrib={ 'isSupported': 'False' if self.ocsp_response is None else 'True' }) if self.ocsp_response: ocsp_resp_xmp = Element('ocspResponse', attrib={ 'isTrustedByMozillaCAStore': str(self.is_ocsp_response_trusted) }) responder_xml = Element('responderID') responder_xml.text = self.ocsp_response['responderID'] ocsp_resp_xmp.append(responder_xml) produced_xml = Element('producedAt') produced_xml.text = self.ocsp_response['producedAt'] ocsp_resp_xmp.append(produced_xml) response_status_xml = Element('responseStatus') response_status_xml.text = self.ocsp_response['responseStatus'] ocsp_resp_xmp.append(response_status_xml) ocsp_xml.append(ocsp_resp_xmp) xml_output.append(ocsp_xml) # All done return xml_output
def as_text(self) -> List[str]: txt_result = [self._format_title(self.scan_command.get_title()), ""] txt_result.append( self._format_subtitle( "Computed HPKP Pins for Server Certificate Chain")) if self.verified_certificate_chain: for index, cert in enumerate(self.verified_certificate_chain, start=0): final_subject = CertificateUtils.get_name_as_short_text( cert.subject) if len(final_subject) > 40: # Make the CN shorter when displaying it final_subject = "{}...".format(final_subject[:40]) txt_result.append( self._PIN_TXT_FORMAT( ("{} - {}".format(index, final_subject)), CertificateUtils.get_hpkp_pin(cert))) txt_result.append("") else: txt_result.append( self._format_field( "ERROR - Could not build verified chain (certificate untrusted?)", "")) txt_result.append("") txt_result.append( self._format_subtitle("Strict-Transport-Security Header")) if self.strict_transport_security_header: txt_result.append( self._format_field( "Max Age:", str(self.strict_transport_security_header.max_age))) txt_result.append( self._format_field( "Include Subdomains:", str(self.strict_transport_security_header. include_subdomains))) txt_result.append( self._format_field( "Preload:", str(self.strict_transport_security_header.preload))) else: txt_result.append(self._format_field(self._HEADER_NOT_SENT_TXT, "")) for header, subtitle in [ (self.public_key_pins_header, "Public-Key-Pins Header"), (self.public_key_pins_report_only_header, "Public-Key-Pins-Report-Only Header"), ]: txt_result.extend(["", self._format_subtitle(subtitle)]) if header: txt_result.append( self._format_field("Max Age:", str(header.max_age))) txt_result.append( self._format_field("Include Subdomains:", str(header.include_subdomains))) txt_result.append( self._format_field("Report URI:", str(header.report_uri))) txt_result.append( self._format_field("SHA-256 Pin List:", ", ".join(header.pin_sha256_list))) if self.verified_certificate_chain: pin_validation_txt = ( "OK - One of the configured pins was found in the certificate chain" if self.is_valid_pin_configured else "FAILED - Could NOT find any of the configured pins in the certificate chain!" ) txt_result.append( self._format_field("Valid Pin:", pin_validation_txt)) backup_txt = ( "OK - Backup pin found in the configured pins" if self.is_backup_pin_configured else "FAILED - No backup pin found: all the configured pins are in the certificate chain!" ) txt_result.append( self._format_field("Backup Pin:", backup_txt)) else: txt_result.append( self._format_field(self._HEADER_NOT_SENT_TXT, "")) txt_result.extend(["", self._format_subtitle("Expect-CT Header")]) if self.expect_ct_header: txt_result.append( self._format_field("Max Age:", str(self.expect_ct_header.max_age))) txt_result.append( self._format_field("Report- URI:", str(self.expect_ct_header.report_uri))) txt_result.append( self._format_field("Enforce:", str(self.expect_ct_header.enforce))) else: txt_result.append(self._format_field(self._HEADER_NOT_SENT_TXT, "")) return txt_result
def as_text(self) -> List[str]: txt_result = [self._format_title(self.scan_command.get_title()), ''] txt_result.append(self._format_subtitle('Computed HPKP Pins for Server Certificate Chain')) if self.verified_certificate_chain: for index, cert in enumerate(self.verified_certificate_chain, start=0): final_subject = CertificateUtils.get_name_as_short_text(cert.subject) if len(final_subject) > 40: # Make the CN shorter when displaying it final_subject = '{}...'.format(final_subject[:40]) txt_result.append( self._PIN_TXT_FORMAT(('{} - {}'.format(index, final_subject)), CertificateUtils.get_hpkp_pin(cert)) ) txt_result.append('') else: txt_result.append( self._format_field('ERROR - Could not build verified chain (certificate untrusted?)', '') ) txt_result.append('') txt_result.append(self._format_subtitle('Strict-Transport-Security Header')) if self.strict_transport_security_header: txt_result.append(self._format_field("Max Age:", str(self.strict_transport_security_header.max_age))) txt_result.append(self._format_field( "Include Subdomains:", str(self.strict_transport_security_header.include_subdomains)) ) txt_result.append(self._format_field("Preload:", str(self.strict_transport_security_header.preload))) else: txt_result.append(self._format_field(self._HEADER_NOT_SENT_TXT, "")) for header, subtitle in [ (self.public_key_pins_header, 'Public-Key-Pins Header'), (self.public_key_pins_report_only_header, 'Public-Key-Pins-Report-Only Header') ]: txt_result.extend(['', self._format_subtitle(subtitle)]) if header: txt_result.append(self._format_field("Max Age:", str(header.max_age))) txt_result.append(self._format_field("Include Subdomains:", str(header.include_subdomains))) txt_result.append(self._format_field("Report URI:", str(header.report_uri))) txt_result.append(self._format_field("SHA-256 Pin List:", ', '.join(header.pin_sha256_list))) if self.verified_certificate_chain: pin_validation_txt = 'OK - One of the configured pins was found in the certificate chain' \ if self.is_valid_pin_configured \ else 'FAILED - Could NOT find any of the configured pins in the certificate chain!' txt_result.append(self._format_field("Valid Pin:", pin_validation_txt)) backup_txt = 'OK - Backup pin found in the configured pins' \ if self.is_backup_pin_configured \ else 'FAILED - No backup pin found: all the configured pins are in the certificate chain!' txt_result.append(self._format_field("Backup Pin:", backup_txt)) else: txt_result.append(self._format_field(self._HEADER_NOT_SENT_TXT, "")) txt_result.extend(['', self._format_subtitle('Expect-CT Header')]) if self.expect_ct_header: txt_result.append(self._format_field('Max Age:', str(self.expect_ct_header.max_age))) txt_result.append(self._format_field('Report- URI:', str(self.expect_ct_header.report_uri))) txt_result.append(self._format_field('Enforce:', str(self.expect_ct_header.enforce))) else: txt_result.append(self._format_field(self._HEADER_NOT_SENT_TXT, "")) return txt_result