def test_fetch_ocsp(self): with open(os.path.join(fixtures_dir, 'GeoTrust_EV_SSL_CA_-_G4.crt'), 'rb') as f: cert_bytes = f.read() if pem.detect(cert_bytes): _, _, cert_bytes = pem.unarmor(cert_bytes) intermediate = x509.Certificate.load(cert_bytes) registry = CertificateRegistry() path = registry.build_paths(intermediate)[0] issuer = path.find_issuer(intermediate) ocsp_response = ocsp_client.fetch(intermediate, issuer, timeout=3) context = ValidationContext(ocsps=[ocsp_response]) verify_ocsp_response(intermediate, path, context)
def test_build_paths_custom_ca_certs(self): with open(os.path.join(fixtures_dir, 'codex.crt'), 'rb') as f: cert_bytes = f.read() if pem.detect(cert_bytes): _, _, cert_bytes = pem.unarmor(cert_bytes) cert = x509.Certificate.load(cert_bytes) with open(os.path.join(fixtures_dir, 'GeoTrust_EV_SSL_CA_-_G4.crt'), 'rb') as f: other_certs = [f.read()] repo = CertificateRegistry(trust_roots=other_certs) paths = repo.build_paths(cert) self.assertEqual(1, len(paths)) path = paths[0] self.assertEqual(2, len(path)) self.assertEqual([ b'\xaa+\x03\x14\xafd.\x13\x0e\xd6\x92%\xe3\xff*\xba\xd7=b0', b"\xfcq\x7f\x98='\xcc\xb3D\xfbK\x85\xf0\x81\x8f\xab\xcb\xf0\x9b\x14" ], [item.subject.sha1 for item in path])
def test_build_paths_custom_ca_certs(self): with open(os.path.join(fixtures_dir, 'mozilla.org.crt'), 'rb') as f: cert_bytes = f.read() if pem.detect(cert_bytes): _, _, cert_bytes = pem.unarmor(cert_bytes) cert = x509.Certificate.load(cert_bytes) with open( os.path.join(fixtures_dir, 'digicert-sha2-secure-server-ca.crt'), 'rb') as f: other_certs = [f.read()] repo = CertificateRegistry(trust_roots=other_certs) paths = repo.build_paths(cert) self.assertEqual(1, len(paths)) path = paths[0] self.assertEqual(2, len(path)) self.assertEqual([ b"\x10_\xa6z\x80\x08\x9d\xb5'\x9f5\xce\x83\x0bC\x88\x9e\xa3\xc7\r", b'I\xac\x03\xf8\xf3Km\xca)V)\xf2I\x9a\x98\xbe\x98\xdc.\x81' ], [item.subject.sha1 for item in path])
def test_build_paths_custom_ca_certs(self): with open(os.path.join(fixtures_dir, 'codex.crt'), 'rb') as f: cert_bytes = f.read() if pem.detect(cert_bytes): _, _, cert_bytes = pem.unarmor(cert_bytes) cert = x509.Certificate.load(cert_bytes) with open(os.path.join(fixtures_dir, 'GeoTrust_EV_SSL_CA_-_G4.crt'), 'rb') as f: other_certs = [f.read()] repo = CertificateRegistry(trust_roots=other_certs) paths = repo.build_paths(cert) self.assertEqual(1, len(paths)) path = paths[0] self.assertEqual(2, len(path)) self.assertEqual( [ b'\xaa+\x03\x14\xafd.\x13\x0e\xd6\x92%\xe3\xff*\xba\xd7=b0', b"\xfcq\x7f\x98='\xcc\xb3D\xfbK\x85\xf0\x81\x8f\xab\xcb\xf0\x9b\x14" ], [item.subject.sha1 for item in path] )
def validate_ocsp_response(cert, issuer, ocsp_request_obj, ocsp_response_objs, current_time): #print(cert.ocsp_urls) # Just to see how many urls there are subject = cert['tbs_certificate']['subject'].native errors = {} warnings = {} lints_list = [] count = 0 lints = {} for (ocsp_url, ocsp_response_obj) in ocsp_response_objs: count += 1 lints['domain'] = subject['common_name'] lints['ocsp_url'] = ocsp_url lints['response_count'] = count #print(ocsp_response_obj.native) errors = {} warnings = {} if isinstance(ocsp_response_obj, urllib.error.HTTPError): errors['HTTPError'] = str(ocsp_response_obj) lints['errors'] = errors lints['warnings'] = warnings lints_list.append(lints) return lints_list if isinstance(ocsp_response_obj, ValueError): errors['ValueError'] = str(ocsp_response_obj) lints['errors'] = errors lints['warnings'] = warnings lints_list.append(lints) return lints_list if (ocsp_response_obj['response_status'].native == 'unauthorized'): errors['Unauthorized'] = 'Responder returned unauthorized' lints['errors'] = errors lints['warnings'] = warnings lints_list.append(lints) continue if (ocsp_response_obj['response_status'].native == 'malformed_request' ): errors['ResponseFailure'] = 'Responder returned malformed request' lints['errors'] = errors lints['warnings'] = warnings lints_list.append(lints) continue request_nonce = ocsp_request_obj.nonce_value #print (ocsp_response_obj.native) response_nonce = ocsp_response_obj.nonce_value if request_nonce and response_nonce and request_nonce.native != response_nonce.native: errors[ 'NonceVerificationFailure'] = 'Unable to verify OCSP response since the request and response nonces do not match' if ocsp_response_obj['response_status'].native != 'successful': errors['OCSPCheckFailure'] = 'OCSP check returned as failed' response_bytes = ocsp_response_obj['response_bytes'] if response_bytes['response_type'].native != 'basic_ocsp_response': errors[ 'ResponseTypeFailure'] = 'OCSP response is not Basic OCSP Response' parsed_response = response_bytes['response'].parsed tbs_response = parsed_response['tbs_response_data'] certificate_response = tbs_response['responses'][0] certificate_id = certificate_response['cert_id'] algo = certificate_id['hash_algorithm']['algorithm'].native certificate_issuer_name_hash = certificate_id[ 'issuer_name_hash'].native certificate_issuer_key_hash = certificate_id['issuer_key_hash'].native certificate_serial_number = certificate_id['serial_number'].native certificate_issuer_name_hash_from_file = getattr(cert.issuer, algo) certificate_issuer_key_hash_from_file = getattr( issuer.public_key, algo) certificate_serial_number_from_file = cert.serial_number if certificate_serial_number != certificate_serial_number_from_file: errors['CertificateSerialMismatchFailure'] = \ 'OCSP response certificate serial number does not match request certificate serial number' if certificate_issuer_key_hash != certificate_issuer_key_hash_from_file: errors[ 'IssuerKeyMismatchFailure'] = 'OCSP response issuer key hash does not match request certificate issuer key hash' if certificate_issuer_name_hash != certificate_issuer_name_hash_from_file: errors['IssuerNameHashMismatchFailure'] = \ 'OCSP response issuer name hash does not match request certificate issuer name hash' this_update_time = certificate_response['this_update'].native if current_time < this_update_time: errors[ 'ThisUpdateTimeError'] = 'OCSP reponse update time is from the future' if "next_update" not in certificate_response or certificate_response[ 'next_update'].native is None: warnings[ 'NextUpdateTimeMissing'] = 'OCSP response does not contain next update time' else: next_update_time = certificate_response['next_update'].native if current_time > next_update_time: errors[ 'NextUpdateTimeFailure'] = 'OCSP reponse next update time is in the past' registry = CertificateRegistry(trust_roots=[issuer]) if tbs_response['responder_id'].name == 'by_key': key_identifier = tbs_response['responder_id'].native signing_cert = registry.retrieve_by_key_identifier(key_identifier) elif tbs_response['responder_id'].name == 'by_name': signing_certs = registry.retrieve_by_name( tbs_response['responder_id'].chosen, None) if signing_certs is not None and len(signing_certs) > 0: signing_cert = signing_certs[0] else: signing_cert = None if signing_cert is None: errors[ 'SigningCetificateNotFoundFailure'] = 'OCSP response signing certificate not found' lints['errors'] = errors lints['warnings'] = warnings lints_list.append(lints) continue if issuer.issuer_serial != signing_cert.issuer_serial: if signing_cert_issuer.issuer_serial != issuer.issuer_serial: errors[ 'UnauthorizedSigningCertificateFailure'] = 'OCSP response signed by unauthorized certificate' extended_key_usage = signing_cert.extended_key_usage_value if 'ocsp_signing' not in extended_key_usage.native: errors['ExtendedKeyUsageExtensionValueFailure'] = \ 'OCSP response signing certificate is not the issuing certificate and it does not have value "ocsp_signing"\ for the extended key usage extension' sig_algo = parsed_response['signature_algorithm'].signature_algo hash_algo = parsed_response['signature_algorithm'].hash_algo try: check_cert = asymmetric.load_certificate(signing_cert) if sig_algo == 'rsassa_pkcs1v15': asymmetric.rsa_pkcs1v15_verify( check_cert, parsed_response['signature'].native, tbs_response.dump(), hash_algo) elif sig_algo == 'dsa': asymmetric.dsa_verify(check_cert, parsed_response['signature'].native, tbs_response.dump(), hash_algo) elif sig_algo == 'ecdsa': asymmetric.ecdsa_verify(check_cert, parsed_response['signature'].native, tbs_response.dump(), hash_algo) else: errors[ 'UnsupportedAlgorithmFailure'] = 'OCSP response signature uses unsupported algorithm' except (oscrypto.errors.SignatureError): errors[ 'SignatureVerificationFailure'] = 'OCSP response signature could not be verified' if certificate_response['cert_status'].name == 'revoked': revocation_data = certificate_response['cert_status'].chosen if revocation_data['revocation_reason'].native is None: errors[ 'CertificateValidityFailure'] = 'Certificate revoked due to unknown reason' else: errors[ 'CertificateValidityFailure'] = 'Certicate revoked due to ' + revocation_data[ 'revocation_reason'].human_friendly if 'certs' in parsed_response and parsed_response[ 'certs'].native != None: #TODO Check for legit certs pass # print(parsed_response['certs'].native) if len(errors) == 0: errors['NoFailure'] = 'No errors in OCSP response' if len(warnings) == 0: warnings['NoWarning'] = 'No warnings in OCSP response' lints['errors'] = errors lints['warnings'] = warnings lints_list.append(lints) return lints_list