from cryptography import x509 from cryptography.hazmat.primitives.asymmetric import padding from datetime import datetime, UTC # type: ignore from cryptography.x509.oid import NameOID def verify_certificate( cert_data, ca_chain_path, correct_organization="[Universität Bremen]" ) -> tuple[bool, str, list[str], str, str, str]: """ Extract and print details about the certificate. :param certificate_path: Path to the certificate """ try: cert = x509.load_pem_x509_certificate(cert_data) for entry in ca_chain_path: with open(entry, "rb") as cert_file: ca_data = cert_file.read() ca_cert = x509.load_pem_x509_certificate(ca_data) ca_public_key = ca_cert.public_key() ca_okay: bool = False error_message: str = "" # Verify the certificate's signature try: # This will raise an exception if the signature is invalid ca_public_key.verify( cert.signature, cert.tbs_certificate_bytes, # Updated method to get TBS bytes padding.PKCS1v15(), cert.signature_hash_algorithm, ) ca_okay = True break except Exception as sig_error: ca_okay = False error_message = f"Signature verification failed: {sig_error}" except FileNotFoundError: ca_okay = False error_message = "Certificate or CA certificate file not found." except Exception as e: ca_okay = False error_message = f"Error verifying certificate authenticity: {e}" if ca_okay is False: return ( False, error_message, [], "", "", "", ) print("Certificate Details:") print(f"Subject: {cert.subject}") print(f"Issuer: {cert.issuer}") print(f"Serial Number: {cert.serial_number}") print(f"Not Valid Before (UTC): {cert.not_valid_before_utc}") print(f"Not Valid After (UTC): {cert.not_valid_after_utc}") # Check if certificate is currently valid current_time = datetime.now(UTC) is_valid = ( current_time >= cert.not_valid_before_utc and current_time <= cert.not_valid_after_utc ) if is_valid is False: return False, f"Currently Valid: {is_valid}", [], "", "", "" email_found = None name_found = None surname_found = None given_name_found = None for attr in cert.subject: if str(NameOID.ORGANIZATION_NAME).lower() == str(attr.oid).lower(): organization_okay: bool = False for org_entries in correct_organization: if str(attr.value).lower() == str(org_entries).lower(): organization_okay = True if organization_okay is False: return ( False, f"Not issued from the correct organization: {str(attr.value)} vs. {str(correct_organization)}", [], "", "", "", ) if str(NameOID.EMAIL_ADDRESS).lower() == str(attr.oid).lower(): email_found = str(attr.value) if str(NameOID.COMMON_NAME).lower() == str(attr.oid).lower(): name_found = str(attr.value) if str(NameOID.SURNAME).lower() == str(attr.oid).lower(): surname_found = str(attr.value) if str(NameOID.GIVEN_NAME).lower() == str(attr.oid).lower(): given_name_found = str(attr.value) san_ext = cert.extensions.get_extension_for_oid( x509.OID_SUBJECT_ALTERNATIVE_NAME ) san_emails = [ name.value for name in san_ext.value if isinstance(name, x509.RFC822Name) ] if email_found is not None: san_emails.append(email_found) san_emails = list(set(san_emails)) if len(san_emails) == 0: return False, "No emails found in certificate", [], "", "", "" if name_found is None: return False, "No common name found in certificate", [], "", "", "" if (surname_found is None) and (given_name_found is None): str_splited = name_found.split(" ") if len(str_splited) > 2: return False, "No surname found in certificate", [], "", "", "" given_name_found = str_splited[0] surname_found = str_splited[1] if surname_found is None: return False, "No surname found in certificate", [], "", "", "" if given_name_found is None: return False, "No given name found in certificate", [], "", "", "" except Exception as e: return False, f"Error extracting certificate details: {e}", [], "", "", "" return True, "", san_emails, name_found, given_name_found, surname_found if __name__ == "__main__": certificate_path = "cert.pem" ca_chain_path = "ca_chain.pem" # Read the certificate file with open(certificate_path, "rb") as cert_file: cert_data = cert_file.read() print(verify_certificate(cert_data=cert_data, ca_chain_path=ca_chain_path))