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')
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
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')
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
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)
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
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)
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()
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, )