Exemplo n.º 1
0
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
Exemplo n.º 2
0
    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
Exemplo n.º 3
0
    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
Exemplo n.º 4
0
    def _get_basic_certificate_text(self) -> List[str]:
        certificate = self.certificate_chain[0]
        public_key = self.certificate_chain[0].public_key()
        text_output = [
            self._format_field('SHA1 Fingerprint:',
                               binascii.hexlify(certificate.fingerprint(hashes.SHA1())).decode('ascii')),
            self._format_field('Common Name:', CertificateUtils.get_name_as_short_text(certificate.subject)),
            self._format_field('Issuer:', CertificateUtils.get_name_as_short_text(certificate.issuer)),
            self._format_field('Serial Number:', certificate.serial_number),
            self._format_field('Not Before:', certificate.not_valid_before),
            self._format_field('Not After:', certificate.not_valid_after),
            self._format_field('Signature Algorithm:', certificate.signature_hash_algorithm.name),
            self._format_field('Public Key Algorithm:', CertificateUtils.get_public_key_type(certificate))]

        if isinstance(public_key, EllipticCurvePublicKey):
            text_output.append(self._format_field('Key Size:', public_key.curve.key_size))
            text_output.append(self._format_field('Curve:', public_key.curve.name))
        elif isinstance(public_key, RSAPublicKey):
            text_output.append(self._format_field('Key Size:', public_key.key_size))
            text_output.append(self._format_field('Exponent:', '{0} (0x{0:x})'.format(public_key.public_numbers().e)))
        else:
            # DSA Key? https://github.com/nabla-c0d3/sslyze/issues/314
            pass

        try:
            # Print the SAN extension if there's one
            text_output.append(self._format_field('DNS Subject Alternative Names:',
                                                  str(CertificateUtils.get_dns_subject_alternative_names(certificate))))
        except KeyError:
            pass

        return text_output
Exemplo n.º 5
0
    def _get_basic_certificate_text(self) -> List[str]:
        certificate = self.certificate_chain[0]
        public_key = self.certificate_chain[0].public_key()
        text_output = [
            self._format_field('SHA1 Fingerprint:',
                               binascii.hexlify(certificate.fingerprint(hashes.SHA1())).decode('ascii')),
            self._format_field('Common Name:', CertificateUtils.get_name_as_short_text(certificate.subject)),
            self._format_field('Issuer:', CertificateUtils.get_name_as_short_text(certificate.issuer)),
            self._format_field('Serial Number:', certificate.serial_number),
            self._format_field('Not Before:', certificate.not_valid_before),
            self._format_field('Not After:', certificate.not_valid_after),
            self._format_field('Signature Algorithm:', certificate.signature_hash_algorithm.name),
            self._format_field('Public Key Algorithm:', CertificateUtils.get_public_key_type(certificate))]

        if isinstance(public_key, EllipticCurvePublicKey):
            text_output.append(self._format_field('Key Size:', public_key.curve.key_size))
            text_output.append(self._format_field('Curve:', public_key.curve.name))
        elif isinstance(public_key, RSAPublicKey):
            text_output.append(self._format_field('Key Size:', public_key.key_size))
            text_output.append(self._format_field('Exponent:', '{0} (0x{0:x})'.format(public_key.public_numbers().e)))
        else:
            # DSA Key? https://github.com/nabla-c0d3/sslyze/issues/314
            pass

        try:
            # Print the SAN extension if there's one
            text_output.append(self._format_field('DNS Subject Alternative Names:',
                                                  str(CertificateUtils.get_dns_subject_alternative_names(certificate))))
        except KeyError:
            pass

        return text_output
Exemplo n.º 6
0
    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
Exemplo n.º 7
0
    def test(self):
        leaf_path = os.path.join(os.path.dirname(__file__), '..', 'utils',
                                 'github.com.pem')
        with open(leaf_path, 'rb') as leaf_file:
            leaf_pem = leaf_file.read()

        certificate = load_pem_x509_certificate(leaf_pem, default_backend())

        self.assertIsNone(
            CertificateUtils.matches_hostname(certificate, 'www.github.com'))
        with self.assertRaises(ssl.CertificateError):
            self.assertFalse(
                CertificateUtils.matches_hostname(certificate,
                                                  'notgithub.com'))

        self.assertEqual(
            CertificateUtils.get_common_names(certificate.subject),
            ['github.com'])
        self.assertEqual(
            CertificateUtils.get_dns_subject_alternative_names(certificate),
            ['github.com', 'www.github.com'])

        self.assertEqual(
            CertificateUtils.get_printable_name(certificate.issuer),
            'DigiCert SHA2 Extended Validation Server CA')
Exemplo n.º 8
0
    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
Exemplo n.º 9
0
    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
Exemplo n.º 10
0
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
Exemplo n.º 11
0
    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)
Exemplo n.º 12
0
    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)
Exemplo n.º 13
0
    def test(self):
        leaf_path = os.path.join(os.path.dirname(__file__), '..', 'utils', 'github.com.pem')
        with open(leaf_path, 'rb') as leaf_file:
            leaf_pem = leaf_file.read()

        certificate = load_pem_x509_certificate(leaf_pem, default_backend())

        self.assertIsNone(CertificateUtils.matches_hostname(certificate, 'www.github.com'))
        with self.assertRaises(ssl.CertificateError):
            self.assertFalse(CertificateUtils.matches_hostname(certificate, 'notgithub.com'))

        self.assertEqual(CertificateUtils.get_common_names(certificate.subject), ['github.com'])
        self.assertEqual(CertificateUtils.get_dns_subject_alternative_names(certificate), ['github.com',
                                                                                           'www.github.com'])

        self.assertEqual(CertificateUtils.get_name_as_short_text(certificate.issuer),
                         'DigiCert SHA2 Extended Validation Server CA')
Exemplo n.º 14
0
    def test(self):
        leaf_path = Path(__file__).absolute().parent / '..' / 'utils' / 'github.com.pem'
        leaf_pem = leaf_path.read_bytes()
        certificate = load_pem_x509_certificate(leaf_pem, default_backend())

        assert CertificateUtils.matches_hostname(certificate, 'www.github.com') is None

        with pytest.raises(ssl.CertificateError):
            assert not CertificateUtils.matches_hostname(certificate, 'notgithub.com')

        assert CertificateUtils.get_common_names(certificate.subject) == ['github.com']
        assert CertificateUtils.get_dns_subject_alternative_names(certificate) == [
            'github.com', 'www.github.com'
        ]

        expected_name = 'DigiCert SHA2 Extended Validation Server CA'
        assert CertificateUtils.get_name_as_short_text(certificate.issuer) == expected_name
Exemplo n.º 15
0
    def _get_basic_certificate_text(self):
        certificate = self.certificate_chain[0]
        public_key = self.certificate_chain[0].public_key()
        text_output = [
            self._format_field(
                'SHA1 Fingerprint:',
                binascii.hexlify(certificate.fingerprint(
                    hashes.SHA1())).decode('ascii')),
            self._format_field(
                'Common Name:',
                CertificateUtils.get_printable_name(certificate.subject)),
            self._format_field(
                'Issuer:',
                CertificateUtils.get_printable_name(certificate.issuer)),
            self._format_field('Serial Number:', certificate.serial_number),
            self._format_field('Not Before:', certificate.not_valid_before),
            self._format_field('Not After:', certificate.not_valid_after),
            self._format_field('Signature Algorithm:',
                               certificate.signature_hash_algorithm.name),
            self._format_field('Public Key Algorithm:',
                               public_key.__class__.__name__),
            self._format_field('Key Size:', public_key.key_size)
        ]

        try:
            # Print the Public key exponent if there's one; EC public keys don't have one for example
            text_output.append(
                self._format_field(
                    'Exponent:',
                    '{0} (0x{0:x})'.format(public_key.public_numbers().e)))
        except KeyError:
            pass

        try:
            # Print the SAN extension if there's one
            text_output.append(
                self._format_field(
                    'DNS Subject Alternative Names:',
                    str(
                        CertificateUtils.get_dns_subject_alternative_names(
                            certificate))))
        except KeyError:
            pass

        return text_output
Exemplo n.º 16
0
    def test(self):
        leaf_path = Path(
            __file__).absolute().parent / '..' / 'utils' / 'github.com.pem'
        leaf_pem = leaf_path.read_bytes()
        certificate = load_pem_x509_certificate(leaf_pem, default_backend())

        assert CertificateUtils.matches_hostname(certificate,
                                                 'www.github.com') is None

        with pytest.raises(ssl.CertificateError):
            assert not CertificateUtils.matches_hostname(
                certificate, 'notgithub.com')

        assert CertificateUtils.get_common_names(
            certificate.subject) == ['github.com']
        assert CertificateUtils.get_dns_subject_alternative_names(
            certificate) == ['github.com', 'www.github.com']

        expected_name = 'DigiCert SHA2 Extended Validation Server CA'
        assert CertificateUtils.get_name_as_short_text(
            certificate.issuer) == expected_name
Exemplo n.º 17
0
    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)
Exemplo n.º 18
0
    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)
Exemplo n.º 19
0
    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
Exemplo n.º 20
0
    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)
Exemplo n.º 21
0
    def get_distrust_timeline(
            cls,
            verified_certificate_chain: List[Certificate]
    ) -> Optional[SymantecDistrustTimelineEnum]:
        has_whitelisted_cert = False
        has_blacklisted_cert = False

        # Is there a Symantec root certificate in the chain?
        for certificate in verified_certificate_chain:
            key_hash = binascii.hexlify(CertificateUtils.get_public_key_sha256(certificate)).decode('ascii')
            if key_hash in cls._CA_KEYS_BLACKLIST:
                has_blacklisted_cert = True
            if key_hash in cls._CA_KEYS_WHITELIST:
                has_whitelisted_cert = True

        distrust_enum = None
        if has_blacklisted_cert and not has_whitelisted_cert:
            leaf_cert = verified_certificate_chain[0]
            if leaf_cert.not_valid_before < datetime(year=2016, month=6, day=1):
                distrust_enum = SymantecDistrustTimelineEnum.MARCH_2018
            else:
                distrust_enum = SymantecDistrustTimelineEnum.SEPTEMBER_2018
        return distrust_enum
Exemplo n.º 22
0
    def get_distrust_timeline(
            cls,
            verified_certificate_chain: List[Certificate]
    ) -> Optional[SymantecDistrustTimelineEnum]:
        has_whitelisted_cert = False
        has_blacklisted_cert = False

        # Is there a Symantec root certificate in the chain?
        for certificate in verified_certificate_chain:
            key_hash = binascii.hexlify(CertificateUtils.get_public_key_sha256(certificate)).decode('ascii')
            if key_hash in cls._CA_KEYS_BLACKLIST:
                has_blacklisted_cert = True
            if key_hash in cls._CA_KEYS_WHITELIST:
                has_whitelisted_cert = True

        distrust_enum = None
        if has_blacklisted_cert and not has_whitelisted_cert:
            leaf_cert = verified_certificate_chain[0]
            if leaf_cert.not_valid_before < datetime(year=2016, month=6, day=1):
                distrust_enum = SymantecDistrustTimelineEnum.MARCH_2018
            else:
                distrust_enum = SymantecDistrustTimelineEnum.SEPTEMBER_2018
        return distrust_enum
Exemplo n.º 23
0
def concurrent_scanner(hn):
    # Setup the server to scan and ensure it is online/reachable
    server_info = server_connectivity_tester(hn)
    if server_info is 'error':
        return

    # Run multiple scan commands concurrently.
    concurrent_scanner = ConcurrentScanner()

    # Queue some scan commands
    print('\nQueuing some commands...')
    concurrent_scanner.queue_scan_command(server_info,
                                          CertificateInfoScanCommand())
    concurrent_scanner.queue_scan_command(server_info, Tlsv13ScanCommand())
    concurrent_scanner.queue_scan_command(server_info, Tlsv12ScanCommand())
    concurrent_scanner.queue_scan_command(server_info, Tlsv11ScanCommand())
    concurrent_scanner.queue_scan_command(server_info, Tlsv10ScanCommand())
    concurrent_scanner.queue_scan_command(server_info, Sslv30ScanCommand())
    concurrent_scanner.queue_scan_command(server_info, Sslv20ScanCommand())

    # Process the results
    print('\nProcessing results...')
    for scan_result in concurrent_scanner.get_results():
        # これからスキャンする情報(コマンド)を表示
        print(
            f'\nReceived result for "{scan_result.scan_command.get_title()}" '
            f'on {scan_result.server_info.hostname}')

        # A scan command can fail (as a bug); it is returned as a PluginRaisedExceptionResult
        # スキャンコマンドのエラー
        if isinstance(scan_result, PluginRaisedExceptionScanResult):
            ##raise RuntimeError(f'Scan command failed: {scan_result.scan_command.get_title()}')
            print(
                f'Scan command failed: {scan_result.scan_command.get_title()}')
            continue

        # Each scan result has attributes with the information yo're looking for
        # All these attributes are documented within each scan command's module
        if isinstance(scan_result.scan_command, Sslv20ScanCommand):
            # Cipher suitesリスト(ssl2.0)を表示
            for cipher in scan_result.accepted_cipher_list:
                print(f'    {cipher.name}')
                sql = "INSERT INTO CipherSuite(hostname, SSL20) values(?, ?)"
                data = [(hn, cipher.name)]
                cur.executemany(sql, data)

        if isinstance(scan_result.scan_command, Sslv30ScanCommand):
            # Cipher suitesリスト(ssl3.0)を表示
            for cipher in scan_result.accepted_cipher_list:
                print(f'    {cipher.name}')
                sql = "INSERT INTO CipherSuite(hostname, SSL30) values(?, ?)"
                data = [(hn, cipher.name)]
                cur.executemany(sql, data)

        if isinstance(scan_result.scan_command, Tlsv10ScanCommand):
            # Cipher suitesリスト(tls1.0)を表示
            for cipher in scan_result.accepted_cipher_list:
                print(f'    {cipher.name}')
                sql = "INSERT INTO CipherSuite(hostname, TLS10) values(?, ?)"
                data = [(hn, cipher.name)]
                cur.executemany(sql, data)

        if isinstance(scan_result.scan_command, Tlsv11ScanCommand):
            # Cipher suitesリスト(tls1.1)を表示
            for cipher in scan_result.accepted_cipher_list:
                print(f'    {cipher.name}')
                sql = "INSERT INTO CipherSuite(hostname, TLS11) values(?, ?)"
                data = [(hn, cipher.name)]
                cur.executemany(sql, data)

        if isinstance(scan_result.scan_command, Tlsv12ScanCommand):
            # Cipher suitesリスト(tls1.2)を表示
            for cipher in scan_result.accepted_cipher_list:
                print(f'    {cipher.name}')
                sql = "INSERT INTO CipherSuite(hostname, TLS12) values(?, ?)"
                data = [(hn, cipher.name)]
                cur.executemany(sql, data)

        if isinstance(scan_result.scan_command, Tlsv13ScanCommand):
            # Cipher suitesリスト(tls1.3)を表示
            for cipher in scan_result.accepted_cipher_list:
                print(f'    {cipher.name}')
                sql = "INSERT INTO CipherSuite(hostname, TLS13) values(?, ?)"
                data = [(hn, cipher.name)]
                cur.executemany(sql, data)

        elif isinstance(scan_result.scan_command, CertificateInfoScanCommand):
            # Print the Common Names within the verified certificate chain
            # 証明書情報を表示
            if not scan_result.verified_certificate_chain:
                print('Error: certificate chain is not trusted!')
                cur.execute("INSERT INTO CertInfo(hostname) values(?)", [hn])

            else:
                print('Certificate chain common names:')
                for cert in scan_result.verified_certificate_chain:
                    cert_common_names_check = cert.subject.get_attributes_for_oid(
                        NameOID.COMMON_NAME)
                    if cert_common_names_check:
                        cert_common_names = cert_common_names_check[0].value
                    else:
                        cert_common_names = ''
                    cert_publickey = CertificateUtils.get_public_key_type(cert)
                    cert_keysize = cert.public_key().key_size
                    cert_sig_algo = cert.signature_algorithm_oid
                    cert_leaf_ev = scan_result.leaf_certificate_is_ev  # leafのみ

                    # Policy type 判定未完成↓
                    """
                    try:
                        cert_policy = cert.extensions.get_extension_for_oid(ExtensionOID.CERTIFICATE_POLICIES).value
                    except ExtensionNotFound:
                        continue
                    OV = '2.23.140.1.2.2'
                    DV = '2.23.140.1.2.1'
                    if OV in cert_policy:
                        cert_policy_type = 'OV'
                    if DV in cert_policy:
                        cert_policy_type = 'DV'
                    else:
                        cert_policy_type = ''
                    """

                    cert_ov_check = cert.subject.get_attributes_for_oid(
                        NameOID.ORGANIZATION_NAME)
                    if cert_ov_check:
                        cert_ov = cert_ov_check[0].value
                    else:
                        cert_ov = ''
                    print(f'   {cert_common_names}')
                    print(f'   {cert_publickey}')
                    print(f'   {cert_keysize}')
                    print(f'   {cert_sig_algo._name}')
                    # print(f'   {cert_policy_type}')
                    print(f'   {cert_leaf_ev}')
                    print(f'   {cert_ov}')

                    sql = "INSERT INTO CertInfo(hostname, commonname, publickey, keysize, signature, certtype, ov) values(?, ?, ?, ?, ?, ?, ?)"
                    data = [
                        (hn, cert_common_names, cert_publickey, cert_keysize,
                         cert_sig_algo._name, cert_leaf_ev, cert_ov)
                    ]
                    cur.executemany(sql, data)
Exemplo n.º 24
0
    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
Exemplo n.º 25
0
    def as_text(self) -> List[str]:
        text_output = [self._format_title(self.scan_command.get_title()), self._format_subtitle('Content')]
        text_output.extend(self._get_basic_certificate_text())

        # Trust section
        text_output.extend(['', self._format_subtitle('Trust')])

        # Hostname validation
        server_name_indication = self.server_info.tls_server_name_indication
        if self.server_info.tls_server_name_indication != self.server_info.hostname:
            text_output.append(self._format_field("SNI enabled with virtual domain:", server_name_indication))

        hostname_validation_text = 'OK - Certificate matches {hostname}'.format(hostname=server_name_indication) \
            if self.certificate_matches_hostname \
            else 'FAILED - Certificate does NOT match {hostname}'.format(hostname=server_name_indication)
        text_output.append(self._format_field('Hostname Validation:', hostname_validation_text))

        # Path validation that was successfully tested
        for path_result in self.path_validation_result_list:
            if path_result.is_certificate_trusted:
                # EV certs - Only Mozilla supported for now
                ev_txt = ''
                if self.is_leaf_certificate_ev and path_result.trust_store.ev_oids:
                    ev_txt = ', Extended Validation'
                path_txt = 'OK - Certificate is trusted{}'.format(ev_txt)

            else:
                path_txt = 'FAILED - Certificate is NOT Trusted: {}'.format(path_result.verify_string)

            text_output.append(self._format_field(
                self.TRUST_FORMAT.format(store_name=path_result.trust_store.name,
                                         store_version=path_result.trust_store.version),
                path_txt))

        # Path validation that ran into errors
        for path_error in self.path_validation_error_list:
            error_txt = 'ERROR: {}'.format(path_error.error_message)
            text_output.append(self._format_field(
                self.TRUST_FORMAT.format(
                    store_name=path_error.trust_store.name,
                    store_version=path_error.trust_store.version
                ),
                error_txt))

        if self.symantec_distrust_timeline is not None:
            timeline_str = 'March 2018' if self.symantec_distrust_timeline == SymantecDistrustTimelineEnum.MARCH_2018 \
                else 'September 2018'
            symantec_str = 'WARNING: Certificate distrusted by Google and Mozilla on {}'.format(timeline_str)
        else:
            symantec_str = 'OK - Not a Symantec-issued certificate'
        text_output.append(self._format_field('Symantec 2018 Deprecation:', symantec_str))

        # Print the Common Names within the certificate chain
        cns_in_certificate_chain = [CertificateUtils.get_name_as_short_text(cert.subject)
                                    for cert in self.certificate_chain]
        text_output.append(self._format_field('Received Chain:', ' --> '.join(cns_in_certificate_chain)))

        # Print the Common Names within the verified certificate chain if validation was successful
        if self.verified_certificate_chain:
            cns_in_certificate_chain = [CertificateUtils.get_name_as_short_text(cert.subject)
                                        for cert in self.verified_certificate_chain]
            verified_chain_txt = ' --> '.join(cns_in_certificate_chain)
        else:
            verified_chain_txt = self.NO_VERIFIED_CHAIN_ERROR_TXT
        text_output.append(self._format_field('Verified Chain:', verified_chain_txt))

        if self.verified_certificate_chain:
            chain_with_anchor_txt = 'OK - Anchor certificate not sent' if not self.has_anchor_in_certificate_chain \
                else 'WARNING - Received certificate chain contains the anchor certificate'
        else:
            chain_with_anchor_txt = self.NO_VERIFIED_CHAIN_ERROR_TXT
        text_output.append(self._format_field('Received Chain Contains Anchor:', chain_with_anchor_txt))

        chain_order_txt = 'OK - Order is valid' if self.is_certificate_chain_order_valid \
            else 'FAILED - Certificate chain out of order!'
        text_output.append(self._format_field('Received Chain Order:', chain_order_txt))

        if self.verified_certificate_chain:
            sha1_text = 'OK - No SHA1-signed certificate in the verified certificate chain' \
                if not self.has_sha1_in_certificate_chain \
                else 'INSECURE - SHA1-signed certificate in the verified certificate chain'
        else:
            sha1_text = self.NO_VERIFIED_CHAIN_ERROR_TXT
        text_output.append(self._format_field('Verified Chain contains SHA1:', sha1_text))

        # Extensions section
        text_output.extend(['', self._format_subtitle('Extensions')])

        # OCSP must-staple
        must_staple_txt = 'OK - Extension present' \
            if self.certificate_has_must_staple_extension \
            else 'NOT SUPPORTED - Extension not found'
        text_output.append(self._format_field('OCSP Must-Staple:', must_staple_txt))

        # Look for SCT extension
        scts_count = self.certificate_included_scts_count
        if scts_count is None:
            sct_txt = 'OK - Extension present'
        elif scts_count == 0:
            sct_txt = 'NOT SUPPORTED - Extension not found'
        elif scts_count < 3:
            sct_txt = 'WARNING - Only {} SCTs included but Google recommends 3 or more'.format(str(scts_count))
        else:
            sct_txt = 'OK - {} SCTs included'.format(str(scts_count))
        text_output.append(self._format_field('Certificate Transparency:', sct_txt))

        # OCSP stapling
        text_output.extend(['', self._format_subtitle('OCSP Stapling')])

        if self.ocsp_response is None:
            text_output.append(self._format_field('', 'NOT SUPPORTED - Server did not send back an OCSP response'))

        else:
            if self.ocsp_response_status != OcspResponseStatusEnum.SUCCESSFUL:
                ocsp_resp_txt = [self._format_field('', 'ERROR - OCSP response status is not successful: {}'.format(
                    self.ocsp_response_status.name  # type: ignore
                ))]
            else:
                ocsp_trust_txt = 'OK - Response is trusted' \
                    if self.is_ocsp_response_trusted \
                    else 'FAILED - Response is NOT trusted'

                ocsp_resp_txt = [
                    self._format_field('OCSP Response Status:', self.ocsp_response['responseStatus']),
                    self._format_field('Validation w/ Mozilla Store:', ocsp_trust_txt),
                    self._format_field('Responder Id:', self.ocsp_response['responderID'])]

                if 'successful' in self.ocsp_response['responseStatus']:
                    ocsp_resp_txt.extend([
                        self._format_field('Cert Status:', self.ocsp_response['responses'][0]['certStatus']),
                        self._format_field('Cert Serial Number:',
                                           self.ocsp_response['responses'][0]['certID']['serialNumber']),
                        self._format_field('This Update:', self.ocsp_response['responses'][0]['thisUpdate']),
                        self._format_field('Next Update:', self.ocsp_response['responses'][0]['nextUpdate'])
                    ])
            text_output.extend(ocsp_resp_txt)

        # All done
        return text_output
Exemplo n.º 26
0
    def __init__(
            self,
            server_info: ServerConnectivityInfo,
            scan_command: CertificateInfoScanCommand,
            certificate_chain: List[Certificate],
            path_validation_result_list: List[PathValidationResult],
            path_validation_error_list: List[PathValidationError],
            ocsp_response: OcspResponse
    ) -> None:
        super().__init__(server_info, scan_command)
        # Find the first trust store that successfully validated the certificate chain
        self.successful_trust_store = None

        # Sort the path_validation_result_list so the same successful_trust_store always get picked for a given server
        # because threading timings change the order of path_validation_result_list
        def sort_function(path_validation: PathValidationResult) -> str:
            return path_validation.trust_store.name.lower()

        path_validation_result_list.sort(key=sort_function)
        for path_result in path_validation_result_list:
            if path_result.is_certificate_trusted:
                self.successful_trust_store = path_result.trust_store

        self.ocsp_response = None
        self.is_ocsp_response_trusted = None
        self.ocsp_response_status = None
        if ocsp_response:
            self.ocsp_response_status = ocsp_response.status
            # We only keep the dictionary as a nassl.OcspResponse is not pickable
            self.ocsp_response = ocsp_response.as_dict()
            if self.successful_trust_store and self.ocsp_response_status == OcspResponseStatusEnum.SUCCESSFUL:
                try:
                    ocsp_response.verify(self.successful_trust_store.path)
                    self.is_ocsp_response_trusted = True
                except OcspResponseNotTrustedError:
                    self.is_ocsp_response_trusted = False

        self.certificate_chain = certificate_chain

        # Check if it is EV - we only have the EV OIDs for Mozilla
        self.is_leaf_certificate_ev = TrustStoresRepository.get_default().get_main_store().is_extended_validation(
            self.certificate_chain[0]
        )

        # Look for the Must-Staple extension
        has_must_staple = CertificateUtils.has_ocsp_must_staple_extension(self.certificate_chain[0])
        self.certificate_has_must_staple_extension = has_must_staple

        # Look for the certificate transparency extension
        self.certificate_included_scts_count = CertificateUtils.count_scts_in_sct_extension(self.certificate_chain[0])

        # Try to build the verified chain
        self.verified_certificate_chain: List[Certificate] = []
        self.is_certificate_chain_order_valid = True
        if self.successful_trust_store:
            try:
                self.verified_certificate_chain = self.successful_trust_store.build_verified_certificate_chain(
                    self.certificate_chain
                )
            except InvalidCertificateChainOrderError:
                self.is_certificate_chain_order_valid = False
            except AnchorCertificateNotInTrustStoreError:
                pass

        self.has_anchor_in_certificate_chain = None
        if self.verified_certificate_chain:
            self.has_anchor_in_certificate_chain = self.verified_certificate_chain[-1] in self.certificate_chain

        self.path_validation_result_list = path_validation_result_list
        self.path_validation_error_list = path_validation_error_list
        try:
            CertificateUtils.matches_hostname(certificate_chain[0], server_info.tls_server_name_indication)
            self.certificate_matches_hostname = True
        except CertificateError:
            self.certificate_matches_hostname = False

        # Check if a SHA1-signed certificate is in the chain
        # Root certificates can still be signed with SHA1 so we only check leaf and intermediate certificates
        self.has_sha1_in_certificate_chain = None
        if self.verified_certificate_chain:
            self.has_sha1_in_certificate_chain = False
            for cert in self.verified_certificate_chain[:-1]:
                if isinstance(cert.signature_hash_algorithm, hashes.SHA1):
                    self.has_sha1_in_certificate_chain = True
                    break

        # Check if this is a distrusted Symantec-issued chain
        self.symantec_distrust_timeline = _SymantecDistructTester.get_distrust_timeline(self.verified_certificate_chain)
Exemplo n.º 27
0
    def __init__(
            self,
            server_info: ServerConnectivityInfo,
            scan_command: CertificateInfoScanCommand,
            certificate_chain: List[Certificate],
            path_validation_result_list: List[PathValidationResult],
            path_validation_error_list: List[PathValidationError],
            ocsp_response: OcspResponse
    ) -> None:
        super().__init__(server_info, scan_command)
        # Find the first trust store that successfully validated the certificate chain
        self.successful_trust_store = None

        # Sort the path_validation_result_list so the same successful_trust_store always get picked for a given server
        # because threading timings change the order of path_validation_result_list
        def sort_function(path_validation: PathValidationResult) -> str:
            return path_validation.trust_store.name.lower()

        path_validation_result_list.sort(key=sort_function)
        for path_result in path_validation_result_list:
            if path_result.is_certificate_trusted:
                self.successful_trust_store = path_result.trust_store

        self.ocsp_response = None
        self.is_ocsp_response_trusted = None
        self.ocsp_response_status = None
        if ocsp_response:
            self.ocsp_response_status = ocsp_response.status
            # We only keep the dictionary as a nassl.OcspResponse is not pickable
            self.ocsp_response = ocsp_response.as_dict()
            if self.successful_trust_store and self.ocsp_response_status == OcspResponseStatusEnum.SUCCESSFUL:
                try:
                    ocsp_response.verify(self.successful_trust_store.path)
                    self.is_ocsp_response_trusted = True
                except OcspResponseNotTrustedError:
                    self.is_ocsp_response_trusted = False

        self.certificate_chain = certificate_chain

        # Check if it is EV - we only have the EV OIDs for Mozilla
        self.is_leaf_certificate_ev = TrustStoresRepository.get_default().get_main_store().is_extended_validation(
            self.certificate_chain[0]
        )

        # Look for the Must-Staple extension
        has_must_staple = CertificateUtils.has_ocsp_must_staple_extension(self.certificate_chain[0])
        self.certificate_has_must_staple_extension = has_must_staple

        # Look for the certificate transparency extension
        self.certificate_included_scts_count = CertificateUtils.count_scts_in_sct_extension(self.certificate_chain[0])

        # Try to build the verified chain
        self.verified_certificate_chain: List[Certificate] = []
        self.is_certificate_chain_order_valid = True
        if self.successful_trust_store:
            try:
                self.verified_certificate_chain = self.successful_trust_store.build_verified_certificate_chain(
                    self.certificate_chain
                )
            except InvalidCertificateChainOrderError:
                self.is_certificate_chain_order_valid = False
            except AnchorCertificateNotInTrustStoreError:
                pass

        self.has_anchor_in_certificate_chain = None
        if self.verified_certificate_chain:
            self.has_anchor_in_certificate_chain = self.verified_certificate_chain[-1] in self.certificate_chain

        self.path_validation_result_list = path_validation_result_list
        self.path_validation_error_list = path_validation_error_list
        try:
            CertificateUtils.matches_hostname(certificate_chain[0], server_info.tls_server_name_indication)
            self.certificate_matches_hostname = True
        except CertificateError:
            self.certificate_matches_hostname = False

        # Check if a SHA1-signed certificate is in the chain
        # Root certificates can still be signed with SHA1 so we only check leaf and intermediate certificates
        self.has_sha1_in_certificate_chain = None
        if self.verified_certificate_chain:
            self.has_sha1_in_certificate_chain = False
            for cert in self.verified_certificate_chain[:-1]:
                if isinstance(cert.signature_hash_algorithm, hashes.SHA1):
                    self.has_sha1_in_certificate_chain = True
                    break

        # Check if this is a distrusted Symantec-issued chain
        self.symantec_distrust_timeline = _SymantecDistructTester.get_distrust_timeline(self.verified_certificate_chain)
Exemplo n.º 28
0
        if isinstance(scan_result.scan_command, Tlsv12ScanCommand):
            # Do something with the result
            print('TLS 1.2 cipher suites')
            for cipher in scan_result.accepted_cipher_list:
                print('    {}'.format(cipher.name))

        elif isinstance(scan_result.scan_command,
                        SessionRenegotiationScanCommand):
            reneg_result = scan_result
            print('Client renegotiation: {}'.format(
                scan_result.accepts_client_renegotiation))
            print('Secure renegotiation: {}'.format(
                scan_result.supports_secure_renegotiation))

        elif isinstance(scan_result.scan_command, CertificateInfoScanCommand):
            # Print the Common Names within the certificate chain
            cns_in_certificate_chain = [
                CertificateUtils.get_name_as_short_text(cert.subject)
                for cert in scan_result.verified_certificate_chain
            ]
            print('Certificate Chain CNn: {}'.format(cns_in_certificate_chain))

    # All the scan command results also always expose two APIs
    # What the SSLyze CLI would output to the console
    print('\nSSLyze text output')
    for line in reneg_result.as_text():
        print(line)
    print('\nSSLyze XML node')
    # The XML node for the SSLyze CLI XML output
    print(reneg_result.as_xml())
Exemplo n.º 29
0
    def as_text(self):
        text_output = [self._format_title(self.COMMAND_TITLE)]
        text_output.extend(self._get_basic_certificate_text())

        # Trust section
        text_output.extend(['', self._format_title('Certificate - Trust')])

        # Hostname validation
        server_name_indication = self.server_info.tls_server_name_indication
        if self.server_info.tls_server_name_indication != self.server_info.hostname:
            text_output.append(
                self._format_field("SNI enabled with virtual domain:",
                                   server_name_indication))

        hostname_validation_text = 'OK - Certificate matches {hostname}'.format(hostname=server_name_indication) \
            if self.certificate_matches_hostname \
            else 'FAILED - Certificate does NOT match {hostname}'.format(hostname=server_name_indication)
        text_output.append(
            self._format_field('Hostname Validation:',
                               hostname_validation_text))

        # Path validation that was successfully tested
        for path_result in self.path_validation_result_list:
            if path_result.is_certificate_trusted:
                # EV certs - Only Mozilla supported for now
                ev_txt = ''
                if self.is_leaf_certificate_ev and TrustStoresRepository.get_main(
                ) == path_result.trust_store:
                    ev_txt = ', Extended Validation'
                path_txt = 'OK - Certificate is trusted{}'.format(ev_txt)

            else:
                path_txt = 'FAILED - Certificate is NOT Trusted: {}'.format(
                    path_result.verify_string)

            text_output.append(
                self._format_field(
                    self.TRUST_FORMAT.format(
                        store_name=path_result.trust_store.name,
                        store_version=path_result.trust_store.version),
                    path_txt))

        # Path validation that ran into errors
        for path_error in self.path_validation_error_list:
            error_txt = 'ERROR: {}'.format(path_error.error_message)
            text_output.append(
                self._format_field(
                    self.TRUST_FORMAT.format(
                        store_name=path_result.trust_store.name,
                        store_version=path_result.trust_store.version),
                    error_txt))

        # Print the Common Names within the certificate chain
        cns_in_certificate_chain = [
            CertificateUtils.get_printable_name(cert.subject)
            for cert in self.certificate_chain
        ]
        text_output.append(
            self._format_field('Received Chain:',
                               ' --> '.join(cns_in_certificate_chain)))

        # Print the Common Names within the verified certificate chain if validation was successful
        if self.verified_certificate_chain:
            cns_in_certificate_chain = [
                CertificateUtils.get_printable_name(cert.subject)
                for cert in self.verified_certificate_chain
            ]
            verified_chain_txt = ' --> '.join(cns_in_certificate_chain)
        else:
            verified_chain_txt = self.NO_VERIFIED_CHAIN_ERROR_TXT
        text_output.append(
            self._format_field('Verified Chain:', verified_chain_txt))

        if self.verified_certificate_chain:
            chain_with_anchor_txt = 'OK - Anchor certificate not sent' if not self.has_anchor_in_certificate_chain \
                else 'WARNING - Received certificate chain contains the anchor certificate'
        else:
            chain_with_anchor_txt = self.NO_VERIFIED_CHAIN_ERROR_TXT
        text_output.append(
            self._format_field('Received Chain Contains Anchor:',
                               chain_with_anchor_txt))

        chain_order_txt = 'OK - Order is valid' if self.is_certificate_chain_order_valid \
            else 'FAILED - Certificate chain out of order!'
        text_output.append(
            self._format_field('Received Chain Order:', chain_order_txt))

        if self.verified_certificate_chain:
            sha1_text = 'OK - No SHA1-signed certificate in the verified certificate chain' \
                if not self.has_sha1_in_certificate_chain \
                else 'INSECURE - SHA1-signed certificate in the verified certificate chain'
        else:
            sha1_text = self.NO_VERIFIED_CHAIN_ERROR_TXT

        text_output.append(
            self._format_field('Verified Chain contains SHA1:', sha1_text))

        # OCSP stapling
        text_output.extend(
            ['', self._format_title('Certificate - OCSP Stapling')])

        if self.ocsp_response is None:
            text_output.append(
                self._format_field(
                    '',
                    'NOT SUPPORTED - Server did not send back an OCSP response.'
                ))

        else:
            ocsp_trust_txt = 'OK - Response is trusted' \
                if self.is_ocsp_response_trusted \
                else 'FAILED - Response is NOT trusted'

            ocsp_resp_txt = [
                self._format_field('OCSP Response Status:',
                                   self.ocsp_response['responseStatus']),
                self._format_field('Validation w/ Mozilla Store:',
                                   ocsp_trust_txt),
                self._format_field('Responder Id:',
                                   self.ocsp_response['responderID'])
            ]

            if 'successful' in self.ocsp_response['responseStatus']:
                ocsp_resp_txt.extend([
                    self._format_field(
                        'Cert Status:',
                        self.ocsp_response['responses'][0]['certStatus']),
                    self._format_field(
                        'Cert Serial Number:', self.ocsp_response['responses']
                        [0]['certID']['serialNumber']),
                    self._format_field(
                        'This Update:',
                        self.ocsp_response['responses'][0]['thisUpdate']),
                    self._format_field(
                        'Next Update:',
                        self.ocsp_response['responses'][0]['nextUpdate'])
                ])
            text_output.extend(ocsp_resp_txt)

        # All done
        return text_output
Exemplo n.º 30
0
    def __init__(
            self,
            server_info,  # type: ServerConnectivityInfo
            scan_command,  # type: CertificateInfoScanCommand
            certificate_chain,  # type: List[cryptography.x509.Certificate]
            path_validation_result_list,  # type: List[PathValidationResult]
            path_validation_error_list,  # type: List[PathValidationError]
            ocsp_response  # type: OcspResponse
    ):
        # type: (...) -> None
        super(CertificateInfoScanResult,
              self).__init__(server_info, scan_command)
        # Find the first trust store that successfully validated the certificate chain
        self.successful_trust_store = None

        # Sort the path_validation_result_list so the same successful_trust_store always get picked for a given server
        # because threading timings change the order of path_validation_result_list
        def sort_function(path_validation):
            # type: (PathValidationResult) -> Text
            return path_validation.trust_store.name.lower()

        path_validation_result_list.sort(key=sort_function)
        for path_result in path_validation_result_list:
            if path_result.is_certificate_trusted:
                self.successful_trust_store = path_result.trust_store

        self.ocsp_response = None
        self.is_ocsp_response_trusted = None
        if ocsp_response:
            # We only keep the dictionary as a nassl.OcspResponse is not pickable
            self.ocsp_response = ocsp_response.as_dict()
            if self.successful_trust_store:
                try:
                    ocsp_response.verify(self.successful_trust_store.path)
                    self.is_ocsp_response_trusted = True
                except OcspResponseNotTrustedError:
                    self.is_ocsp_response_trusted = False

        self.certificate_chain = certificate_chain

        # Check if it is EV - we only have the EV OIDs for Mozilla
        self.is_leaf_certificate_ev = TrustStoresRepository.get_main(
        ).is_extended_validation(self.certificate_chain[0])

        # Try to build the verified chain
        self.verified_certificate_chain = []
        self.is_certificate_chain_order_valid = True
        if self.successful_trust_store:
            try:
                self.verified_certificate_chain = self.successful_trust_store.build_verified_certificate_chain(
                    self.certificate_chain)
            except InvalidCertificateChainOrderError:
                self.is_certificate_chain_order_valid = False
            except AnchorCertificateNotInTrustStoreError:
                pass

        self.has_anchor_in_certificate_chain = None
        if self.verified_certificate_chain:
            self.has_anchor_in_certificate_chain = self.verified_certificate_chain[
                -1] in self.certificate_chain

        self.path_validation_result_list = path_validation_result_list
        self.path_validation_error_list = path_validation_error_list
        try:
            CertificateUtils.matches_hostname(
                certificate_chain[0], server_info.tls_server_name_indication)
            self.certificate_matches_hostname = True
        except CertificateError:
            self.certificate_matches_hostname = False

        # Check if a SHA1-signed certificate is in the chain
        # Root certificates can still be signed with SHA1 so we only check leaf and intermediate certificates
        self.has_sha1_in_certificate_chain = None
        if self.verified_certificate_chain:
            self.has_sha1_in_certificate_chain = False
            for cert in self.verified_certificate_chain[:-1]:
                if isinstance(cert.signature_hash_algorithm, hashes.SHA1):
                    self.has_sha1_in_certificate_chain = True
                    break
Exemplo n.º 31
0
        if isinstance(scan_result.scan_command, Tlsv12ScanCommand):
            # Do something with the result
            print('TLS 1.2 cipher suites')
            for cipher in scan_result.accepted_cipher_list:
                print('    {}'.format(cipher.name))

        elif isinstance(scan_result.scan_command,
                        SessionRenegotiationScanCommand):
            reneg_result = scan_result
            print('Client renegotiation: {}'.format(
                scan_result.accepts_client_renegotiation))
            print('Secure renegotiation: {}'.format(
                scan_result.supports_secure_renegotiation))

        elif isinstance(scan_result.scan_command, CertificateInfoScanCommand):
            # Print the Common Names within the certificate chain
            cns_in_certificate_chain = [
                CertificateUtils.get_printable_name(cert.subject)
                for cert in scan_result.verified_certificate_chain
            ]
            print('Certificate Chain CNn: {}'.format(cns_in_certificate_chain))

    # All the scan command results also always expose two APIs
    # What the SSLyze CLI would output to the console
    print('\nSSLyze text output')
    for line in reneg_result.as_text():
        print(line)
    print('\nSSLyze XML node')
    # The XML node for the SSLyze CLI XML output
    print(reneg_result.as_xml())
Exemplo n.º 32
0
        # Each scan result has attributes with the information yo're looking for, specific to each scan command
        # All these attributes are documented within each scan command's module
        if isinstance(scan_result.scan_command, Tlsv12ScanCommand):
            # Do something with the result
            print('TLS 1.2 cipher suites')
            for cipher in scan_result.accepted_cipher_list:
                print('    {}'.format(cipher.name))

        elif isinstance(scan_result.scan_command, SessionRenegotiationScanCommand):
            reneg_result = scan_result
            print('Client renegotiation: {}'.format(scan_result.accepts_client_renegotiation))
            print('Secure renegotiation: {}'.format(scan_result.supports_secure_renegotiation))

        elif isinstance(scan_result.scan_command, CertificateInfoScanCommand):
            # Print the Common Names within the certificate chain
            cns_in_certificate_chain = [CertificateUtils.get_name_as_short_text(cert.subject)
                                        for cert in scan_result.verified_certificate_chain]
            print('Certificate Chain CNn: {}'.format(cns_in_certificate_chain))


    # All the scan command results also always expose two APIs
    # What the SSLyze CLI would output to the console
    print('\nSSLyze text output')
    for line in reneg_result.as_text():
        print(line)
    print('\nSSLyze XML node')
    # The XML node for the SSLyze CLI XML output
    print(reneg_result.as_xml())

Exemplo n.º 33
0
    def as_text(self) -> List[str]:
        text_output = [self._format_title(self.scan_command.get_title()), self._format_subtitle('Content')]
        text_output.extend(self._get_basic_certificate_text())

        # Trust section
        text_output.extend(['', self._format_subtitle('Trust')])

        # Hostname validation
        server_name_indication = self.server_info.tls_server_name_indication
        if self.server_info.tls_server_name_indication != self.server_info.hostname:
            text_output.append(self._format_field("SNI enabled with virtual domain:", server_name_indication))

        hostname_validation_text = 'OK - Certificate matches {hostname}'.format(hostname=server_name_indication) \
            if self.certificate_matches_hostname \
            else 'FAILED - Certificate does NOT match {hostname}'.format(hostname=server_name_indication)
        text_output.append(self._format_field('Hostname Validation:', hostname_validation_text))

        # Path validation that was successfully tested
        for path_result in self.path_validation_result_list:
            if path_result.is_certificate_trusted:
                # EV certs - Only Mozilla supported for now
                ev_txt = ''
                if self.is_leaf_certificate_ev and path_result.trust_store.ev_oids:
                    ev_txt = ', Extended Validation'
                path_txt = 'OK - Certificate is trusted{}'.format(ev_txt)

            else:
                path_txt = 'FAILED - Certificate is NOT Trusted: {}'.format(path_result.verify_string)

            text_output.append(self._format_field(
                self.TRUST_FORMAT.format(store_name=path_result.trust_store.name,
                                         store_version=path_result.trust_store.version),
                path_txt))

        # Path validation that ran into errors
        for path_error in self.path_validation_error_list:
            error_txt = 'ERROR: {}'.format(path_error.error_message)
            text_output.append(self._format_field(
                self.TRUST_FORMAT.format(
                    store_name=path_error.trust_store.name,
                    store_version=path_error.trust_store.version
                ),
                error_txt))

        if self.symantec_distrust_timeline is not None:
            timeline_str = 'March 2018' if self.symantec_distrust_timeline == SymantecDistrustTimelineEnum.MARCH_2018 \
                else 'September 2018'
            symantec_str = 'WARNING: Certificate distrusted by Google and Mozilla on {}'.format(timeline_str)
        else:
            symantec_str = 'OK - Not a Symantec-issued certificate'
        text_output.append(self._format_field('Symantec 2018 Deprecation:', symantec_str))

        # Print the Common Names within the certificate chain
        cns_in_certificate_chain = [CertificateUtils.get_name_as_short_text(cert.subject)
                                    for cert in self.certificate_chain]
        text_output.append(self._format_field('Received Chain:', ' --> '.join(cns_in_certificate_chain)))

        # Print the Common Names within the verified certificate chain if validation was successful
        if self.verified_certificate_chain:
            cns_in_certificate_chain = [CertificateUtils.get_name_as_short_text(cert.subject)
                                        for cert in self.verified_certificate_chain]
            verified_chain_txt = ' --> '.join(cns_in_certificate_chain)
        else:
            verified_chain_txt = self.NO_VERIFIED_CHAIN_ERROR_TXT
        text_output.append(self._format_field('Verified Chain:', verified_chain_txt))

        if self.verified_certificate_chain:
            chain_with_anchor_txt = 'OK - Anchor certificate not sent' if not self.has_anchor_in_certificate_chain \
                else 'WARNING - Received certificate chain contains the anchor certificate'
        else:
            chain_with_anchor_txt = self.NO_VERIFIED_CHAIN_ERROR_TXT
        text_output.append(self._format_field('Received Chain Contains Anchor:', chain_with_anchor_txt))

        chain_order_txt = 'OK - Order is valid' if self.is_certificate_chain_order_valid \
            else 'FAILED - Certificate chain out of order!'
        text_output.append(self._format_field('Received Chain Order:', chain_order_txt))

        if self.verified_certificate_chain:
            sha1_text = 'OK - No SHA1-signed certificate in the verified certificate chain' \
                if not self.has_sha1_in_certificate_chain \
                else 'INSECURE - SHA1-signed certificate in the verified certificate chain'
        else:
            sha1_text = self.NO_VERIFIED_CHAIN_ERROR_TXT
        text_output.append(self._format_field('Verified Chain contains SHA1:', sha1_text))

        # Extensions section
        text_output.extend(['', self._format_subtitle('Extensions')])

        # OCSP must-staple
        must_staple_txt = 'OK - Extension present' \
            if self.certificate_has_must_staple_extension \
            else 'NOT SUPPORTED - Extension not found'
        text_output.append(self._format_field('OCSP Must-Staple:', must_staple_txt))

        # Look for SCT extension
        scts_count = self.certificate_included_scts_count
        if scts_count is None:
            sct_txt = 'OK - Extension present'
        elif scts_count == 0:
            sct_txt = 'NOT SUPPORTED - Extension not found'
        elif scts_count < 3:
            sct_txt = 'WARNING - Only {} SCTs included but Google recommends 3 or more'.format(str(scts_count))
        else:
            sct_txt = 'OK - {} SCTs included'.format(str(scts_count))
        text_output.append(self._format_field('Certificate Transparency:', sct_txt))

        # OCSP stapling
        text_output.extend(['', self._format_subtitle('OCSP Stapling')])

        if self.ocsp_response is None:
            text_output.append(self._format_field('', 'NOT SUPPORTED - Server did not send back an OCSP response'))

        else:
            if self.ocsp_response_status != OcspResponseStatusEnum.SUCCESSFUL:
                ocsp_resp_txt = [self._format_field('', 'ERROR - OCSP response status is not successful: {}'.format(
                    self.ocsp_response_status.name  # type: ignore
                ))]
            else:
                ocsp_trust_txt = 'OK - Response is trusted' \
                    if self.is_ocsp_response_trusted \
                    else 'FAILED - Response is NOT trusted'

                ocsp_resp_txt = [
                    self._format_field('OCSP Response Status:', self.ocsp_response['responseStatus']),
                    self._format_field('Validation w/ Mozilla Store:', ocsp_trust_txt),
                    self._format_field('Responder Id:', self.ocsp_response['responderID'])]

                if 'successful' in self.ocsp_response['responseStatus']:
                    ocsp_resp_txt.extend([
                        self._format_field('Cert Status:', self.ocsp_response['responses'][0]['certStatus']),
                        self._format_field('Cert Serial Number:',
                                           self.ocsp_response['responses'][0]['certID']['serialNumber']),
                        self._format_field('This Update:', self.ocsp_response['responses'][0]['thisUpdate']),
                        self._format_field('Next Update:', self.ocsp_response['responses'][0]['nextUpdate'])
                    ])
            text_output.extend(ocsp_resp_txt)

        # All done
        return text_output
Exemplo n.º 34
0
    def perform(self) -> CertificateChainDeploymentAnalysisResult:
        """Run the analysis.
        """
        leaf_cert = self.received_certificate_chain[0]

        # OCSP Must-Staple
        has_ocsp_must_staple = False
        try:
            tls_feature_ext = leaf_cert.extensions.get_extension_for_oid(ExtensionOID.TLS_FEATURE)
            for feature_type in tls_feature_ext.value:
                if feature_type == cryptography.x509.TLSFeatureType.status_request:
                    has_ocsp_must_staple = True
                    break
        except ExtensionNotFound:
            pass

        # Received chain order
        is_chain_order_valid = True
        previous_issuer = None
        for index, cert in enumerate(self.received_certificate_chain):
            current_subject = cert.subject

            if index > 0:
                # Compare the current subject with the previous issuer in the chain
                if current_subject != previous_issuer:
                    is_chain_order_valid = False
                    break
            try:
                previous_issuer = cert.issuer
            except KeyError:
                # Missing issuer; this is okay if this is the last cert
                previous_issuer = u"missing issuer {}".format(index)

        # Check if it is EV - we only have the EV OIDs for Mozilla
        is_leaf_certificate_ev = TrustStoresRepository.get_default().get_main_store().is_extended_validation(
            self.received_certificate_chain[0]
        )

        # Check for Signed Timestamps
        number_of_scts: Optional[int] = 0
        try:
            # Look for the x509 extension
            sct_ext = leaf_cert.extensions.get_extension_for_oid(
                ExtensionOID.PRECERT_SIGNED_CERTIFICATE_TIMESTAMPS
            )

            if isinstance(sct_ext.value, cryptography.x509.UnrecognizedExtension):
                # The version of OpenSSL on the system is too old and can't parse the SCT extension
                number_of_scts = None

            # Count the number of entries in the extension
            number_of_scts = len(sct_ext.value)
        except ExtensionNotFound:
            pass

        # Check if the anchor was sent by the server
        has_anchor_in_certificate_chain = None
        if self.verified_certificate_chain:
            has_anchor_in_certificate_chain = self.verified_certificate_chain[-1] in self.received_certificate_chain

        # Check hostname validation
        try:
            CertificateUtils.matches_hostname(leaf_cert, self.server_hostname)
            certificate_matches_hostname = True
        except CertificateError:
            certificate_matches_hostname = False

        # Check if a SHA1-signed certificate is in the chain
        # Root certificates can still be signed with SHA1 so we only check leaf and intermediate certificates
        has_sha1_in_certificate_chain = None
        if self.verified_certificate_chain:
            has_sha1_in_certificate_chain = False
            for cert in self.verified_certificate_chain[:-1]:
                if isinstance(cert.signature_hash_algorithm, hashes.SHA1):
                    has_sha1_in_certificate_chain = True
                    break

        # Check if this is a distrusted Symantec-issued chain
        verified_chain_has_legacy_symantec_anchor = None
        if self.verified_certificate_chain:
            symantec_distrust_timeline = _SymantecDistructTester.get_distrust_timeline(self.verified_certificate_chain)
            verified_chain_has_legacy_symantec_anchor = True if symantec_distrust_timeline else False

        # Check the OCSP response if there is one
        is_ocsp_response_trusted = None
        ocsp_response_status = None
        if self.received_ocsp_response:
            ocsp_response_status = self.received_ocsp_response.status
            if self.trust_store_used_to_build_verified_chain \
                    and ocsp_response_status == OcspResponseStatusEnum.SUCCESSFUL:
                try:
                    self.received_ocsp_response.verify(self.trust_store_used_to_build_verified_chain.path)
                    is_ocsp_response_trusted = True
                except OcspResponseNotTrustedError:
                    is_ocsp_response_trusted = False

        return CertificateChainDeploymentAnalysisResult(
            leaf_certificate_subject_matches_hostname=certificate_matches_hostname,
            leaf_certificate_has_must_staple_extension=has_ocsp_must_staple,
            leaf_certificate_is_ev=is_leaf_certificate_ev,
            leaf_certificate_signed_certificate_timestamps_count=number_of_scts,
            received_chain_contains_anchor_certificate=has_anchor_in_certificate_chain,
            received_chain_has_valid_order=is_chain_order_valid,
            verified_chain_has_sha1_signature=has_sha1_in_certificate_chain,
            verified_chain_has_legacy_symantec_anchor=verified_chain_has_legacy_symantec_anchor,
            ocsp_response_is_trusted=is_ocsp_response_trusted,
            ocsp_response_status=ocsp_response_status,
        )
Exemplo n.º 35
0
    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
Exemplo n.º 36
0
        # Each scan result has attributes with the information yo're looking for, specific to each scan command
        # All these attributes are documented within each scan command's module
        if isinstance(scan_result.scan_command, Tlsv12ScanCommand):
            # Do something with the result
            print('TLS 1.2 cipher suites')
            for cipher in scan_result.accepted_cipher_list:
                print('    {}'.format(cipher.name))

        elif isinstance(scan_result.scan_command, SessionRenegotiationScanCommand):
            reneg_result = scan_result
            print('Client renegotiation: {}'.format(scan_result.accepts_client_renegotiation))
            print('Secure renegotiation: {}'.format(scan_result.supports_secure_renegotiation))

        elif isinstance(scan_result.scan_command, CertificateInfoScanCommand):
            # Print the Common Names within the certificate chain
            cns_in_certificate_chain = [CertificateUtils.get_name_as_short_text(cert.subject)
                                        for cert in scan_result.verified_certificate_chain]
            print('Certificate Chain CNn: {}'.format(cns_in_certificate_chain))


    # All the scan command results also always expose two APIs
    # What the SSLyze CLI would output to the console
    print('\nSSLyze text output')
    for line in reneg_result.as_text():
        print(line)
    print('\nSSLyze XML node')
    # The XML node for the SSLyze CLI XML output
    print(reneg_result.as_xml())

def sslyzeScan(threadname, connection, url):

    logfile_connection = 'log/' + scriptname + '-error-connections.log'
    logfile_other = 'log/' + scriptname + '-error-other.log'
    logfile_scan = 'log/' + scriptname + '-error-other.log'

    has_ip = 0
    is_reachable = 0

    has_ip = getIP(url)

    if has_ip:
        try:
            server_tester = ServerConnectivityTester(hostname=url)
            server_info = server_tester.perform()
            is_reachable = 1
        except ServerConnectivityError:
            with open(logfile_connection, 'a') as log:
                log.write('Error, ' + time.strftime("%Y-%m-%d %H:%M:%S") +
                          ', ' + threadname + ': Could not connect to host: ' +
                          url + '\n')
        except Exception:
            with open(logfile_other, 'a') as log:
                log.write('Error, ' + time.strftime("%Y-%m-%d %H:%M:%S") +
                          ', ' + threadname + ': Thrown error for host: ' +
                          url + '\n')
    else:
        with open(logfile_connection, 'a') as log:
            log.write('Error, ' + time.strftime("%Y-%m-%d %H:%M:%S") + ', ' +
                      threadname + ': Could not resolve host: ' + url + '\n')

    if (is_reachable):
        concurrent_scanner = ConcurrentScanner()
        concurrent_scanner.queue_scan_command(server_info, Sslv20ScanCommand())
        concurrent_scanner.queue_scan_command(server_info, Sslv30ScanCommand())
        concurrent_scanner.queue_scan_command(server_info, Tlsv10ScanCommand())
        concurrent_scanner.queue_scan_command(server_info, Tlsv11ScanCommand())
        concurrent_scanner.queue_scan_command(server_info, Tlsv12ScanCommand())
        concurrent_scanner.queue_scan_command(server_info, Tlsv13ScanCommand())
        concurrent_scanner.queue_scan_command(server_info,
                                              HeartbleedScanCommand())
        concurrent_scanner.queue_scan_command(server_info,
                                              HttpHeadersScanCommand())
        concurrent_scanner.queue_scan_command(server_info,
                                              CertificateInfoScanCommand())

        # Process the results
        for scan_result in concurrent_scanner.get_results():

            # Sometimes a scan command can unexpectedly fail (as a bug); it is returned as a PluginRaisedExceptionResult
            if isinstance(scan_result, PluginRaisedExceptionScanResult):
                with open(logfile_scan, 'a') as log:
                    log.write('Error, ' + time.strftime("%Y-%m-%d %H:%M:%S") +
                              ', Scan command failed: {}'.format(
                                  scan_result.as_text()) + '\n')

            if isinstance(scan_result.scan_command, Sslv20ScanCommand):
                ssl_version = "sslv2"
                try:
                    if len(scan_result.accepted_cipher_list) == 0:
                        supports_sslv2 = 0
                    else:
                        supports_sslv2 = 1
                        for cipher in scan_result.accepted_cipher_list:
                            cipher = (u'{}'.format(cipher.name))
                            sql_command = (
                                'insert into ' + tbl_supported_ciphers +
                                '(url,cipher,version) values (%s,%s,%s)')
                            sql_data = (url, cipher, ssl_version)
                            cur0 = connection.cursor()
                            cur0.execute(sql_command, sql_data)
                            connection.commit()
                            cur0.close()
                except AttributeError:
                    with open(logfile_scan, 'a') as log:
                        log.write(
                            'Error, ' + time.strftime("%Y-%m-%d %H:%M:%S") +
                            ', ' + threadname +
                            ': Could not get sslv2 attributes for host: ' +
                            url + '\n')
                    supports_sslv2 = 0

            if isinstance(scan_result.scan_command, Sslv30ScanCommand):
                ssl_version = "sslv3"
                try:
                    if len(scan_result.accepted_cipher_list) == 0:
                        supports_sslv3 = 0
                    else:
                        supports_sslv3 = 1
                        for cipher in scan_result.accepted_cipher_list:
                            cipher = (u'{}'.format(cipher.name))
                            sql_command = (
                                'insert into ' + tbl_supported_ciphers +
                                '(url,cipher,version) values (%s,%s,%s)')
                            sql_data = (url, cipher, ssl_version)
                            cur0 = connection.cursor()
                            cur0.execute(sql_command, sql_data)
                            connection.commit()
                            cur0.close()
                except AttributeError:
                    with open(logfile_scan, 'a') as log:
                        log.write(
                            'Error, ' + time.strftime("%Y-%m-%d %H:%M:%S") +
                            ', ' + threadname +
                            ': Could not get sslv3 attributes for host: ' +
                            url + '\n')
                    supports_sslv3 = 0

            if isinstance(scan_result.scan_command, Tlsv10ScanCommand):
                ssl_version = "tlsv10"
                try:
                    if len(scan_result.accepted_cipher_list) == 0:
                        supports_tlsv10 = 0
                    else:
                        supports_tlsv10 = 1
                        for cipher in scan_result.accepted_cipher_list:
                            cipher = (u'{}'.format(cipher.name))
                            sql_command = (
                                'insert into ' + tbl_supported_ciphers +
                                '(url,cipher,version) values (%s,%s,%s)')
                            sql_data = (url, cipher, ssl_version)
                            cur0 = connection.cursor()
                            cur0.execute(sql_command, sql_data)
                            connection.commit()
                            cur0.close()
                except AttributeError:
                    with open(logfile_scan, 'a') as log:
                        log.write(
                            'Error, ' + time.strftime("%Y-%m-%d %H:%M:%S") +
                            ', ' + threadname +
                            ': Could not get tlsv10 attributes for host: ' +
                            url + '\n')
                    supports_tlsv10 = 0

            if isinstance(scan_result.scan_command, Tlsv11ScanCommand):
                ssl_version = "tlsv11"
                try:
                    if len(scan_result.accepted_cipher_list) == 0:
                        supports_tlsv11 = 0
                    else:
                        supports_tlsv11 = 1
                        for cipher in scan_result.accepted_cipher_list:
                            cipher = (u'{}'.format(cipher.name))
                            sql_command = (
                                'insert into ' + tbl_supported_ciphers +
                                '(url,cipher,version) values (%s,%s,%s)')
                            sql_data = (url, cipher, ssl_version)
                            cur0 = connection.cursor()
                            cur0.execute(sql_command, sql_data)
                            connection.commit()
                            cur0.close()
                except AttributeError:
                    with open(logfile_scan, 'a') as log:
                        log.write(
                            'Error, ' + time.strftime("%Y-%m-%d %H:%M:%S") +
                            ', ' + threadname +
                            ': Could not get tlsv11 attributes for host: ' +
                            url + '\n')
                    supports_tlsv11 = 0

            if isinstance(scan_result.scan_command, Tlsv12ScanCommand):
                ssl_version = "tlsv12"
                try:
                    if len(scan_result.accepted_cipher_list) == 0:
                        supports_tlsv12 = 0
                    else:
                        supports_tlsv12 = 1
                        for cipher in scan_result.accepted_cipher_list:
                            cipher = (u'{}'.format(cipher.name))
                            sql_command = (
                                'insert into ' + tbl_supported_ciphers +
                                '(url,cipher,version) values (%s,%s,%s)')
                            sql_data = (url, cipher, ssl_version)
                            cur0 = connection.cursor()
                            cur0.execute(sql_command, sql_data)
                            connection.commit()
                            cur0.close()
                except AttributeError:
                    with open(logfile_scan, 'a') as log:
                        log.write(
                            'Error, ' + time.strftime("%Y-%m-%d %H:%M:%S") +
                            ', ' + threadname +
                            ': Could not get tlsv12 attributes for host: ' +
                            url + '\n')
                    supports_tlsv12 = 0

            if isinstance(scan_result.scan_command, Tlsv13ScanCommand):
                ssl_version = "tlsv13"
                try:
                    if len(scan_result.accepted_cipher_list) == 0:
                        supports_tlsv13 = 0
                    else:
                        supports_tlsv13 = 1
                        for cipher in scan_result.accepted_cipher_list:
                            cipher = (u'{}'.format(cipher.name))
                            sql_command = (
                                'insert into ' + tbl_supported_ciphers +
                                '(url,cipher,version) values (%s,%s,%s)')
                            sql_data = (url, cipher, ssl_version)
                            cur0 = connection.cursor()
                            cur0.execute(sql_command, sql_data)
                            connection.commit()
                            cur0.close()
                except AttributeError:
                    with open(logfile_scan, 'a') as log:
                        log.write(
                            'Error, ' + time.strftime("%Y-%m-%d %H:%M:%S") +
                            ', ' + threadname +
                            ': Could not get tlsv13 attributes for host: ' +
                            url + '\n')
                    supports_tlsv13 = 0

            if isinstance(scan_result.scan_command, HeartbleedScanCommand):
                vulnerable_heartbleed = 0
                try:
                    if (scan_result.is_vulnerable_to_heartbleed):
                        vulnerable_heartbleed = 1
                except AttributeError:
                    with open(logfile_scan, 'a') as log:
                        log.write(
                            'Error, ' + time.strftime("%Y-%m-%d %H:%M:%S") +
                            ', ' + threadname +
                            ': Could not get heartbleed attribute for host: ' +
                            url + '\n')
                    vulnerable_heartbleed = 0

            if isinstance(scan_result.scan_command, HttpHeadersScanCommand):
                hsts_preload_set = 0
                hsts_include_subdomains_set = 0
                hsts_max_age_set = 0
                hsts_supported = 0
                hpkp_supported = 0

                try:
                    if (scan_result.hsts_header):
                        hsts_supported = 1
                        if (scan_result.hsts_header.preload):
                            hsts_preload_set = 1
                        if (scan_result.hsts_header.include_subdomains
                            ) == True:
                            hsts_include_subdomains_set = 1
                        hsts_max_age_set = scan_result.hsts_header.max_age
                except AttributeError:
                    with open(logfile_scan, 'a') as log:
                        log.write(
                            'Error, ' + time.strftime("%Y-%m-%d %H:%M:%S") +
                            ', ' + threadname +
                            ': Could not get hsts attributes for host: ' +
                            url + '\n')
                    hsts_preload_set = 0
                    hsts_include_subdomains_set = 0
                    hsts_max_age_set = 0
                    hsts_supported = 0

                try:
                    if (scan_result.hpkp_header):
                        hpkp_supported = 1
                except AttributeError:
                    hpkp_supported = 0

            if isinstance(scan_result.scan_command,
                          CertificateInfoScanCommand):
                chain_is_trusted = 0
                try:
                    if (scan_result.verified_certificate_chain):
                        chain_is_trusted = 1
                except AttributeError:
                    with open(logfile_scan, 'a') as log:
                        log.write(
                            'Error, ' + time.strftime("%Y-%m-%d %H:%M:%S") +
                            ', ' + threadname +
                            ': Could not get hpkp attributes for host: ' +
                            url + '\n')
                    chain_is_trusted = 0

                cert_matches_hostname = 0
                cert_is_ev = False
                try:
                    CertificateUtils.matches_hostname(
                        scan_result.certificate_chain[0],
                        server_info.tls_server_name_indication)
                    cert_matches_hostname = 1
                except CertificateError:
                    cert_matches_hostname = 0
                except AttributeError:
                    with open(logfile_scan, 'a') as log:
                        log.write(
                            'Error, ' + time.strftime("%Y-%m-%d %H:%M:%S") +
                            ', ' + threadname +
                            ': Could not get certificate_chain attribute for host: '
                            + url + '\n')
                try:
                    cert_is_ev = TrustStoresRepository.get_default(
                    ).get_main_store().is_extended_validation(
                        scan_result.certificate_chain[0])
                except AttributeError:
                    with open(logfile_scan, 'a') as log:
                        log.write(
                            'Error, ' + time.strftime("%Y-%m-%d %H:%M:%S") +
                            ', ' + threadname +
                            ': Could not get extended_validation attribute for host: '
                            + url + '\n')

        sql_cmd = (
            'insert into ' + tbl_ssl_options +
            '(url,heartbleed_vulnerable,hsts_supported,hsts_preload_set,hsts_include_subdomains_set,hsts_max_age_set,hpkp_supported,chain_is_trusted,match_hostname,is_ev,sslv2,sslv3,tlsv10,tlsv11,tlsv12,tlsv13) values (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)'
        )
        sql_dat = (url, vulnerable_heartbleed, hsts_supported,
                   hsts_preload_set, hsts_include_subdomains_set,
                   hsts_max_age_set, hpkp_supported, chain_is_trusted,
                   cert_matches_hostname, cert_is_ev, supports_sslv2,
                   supports_sslv3, supports_tlsv10, supports_tlsv11,
                   supports_tlsv12, supports_tlsv13)
        cur = connection.cursor()
        cur.execute(sql_cmd, sql_dat)
        connection.commit()
        cur.close()
Exemplo n.º 38
0
    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
Exemplo n.º 39
0
    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