Exemplo n.º 1
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__)
        ]

        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))
        else:
            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)))

        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.º 2
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.º 3
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, cryptography.hazmat.backends.openssl.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_printable_name(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.º 4
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_printable_name(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 last
        txt_result.extend(computed_hpkp_pins_text)

        return txt_result
Exemplo n.º 5
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.º 6
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())