smime_unibremen_ldap_exchan.../webapp/data/functions/verify_certificate.py

157 lines
5.4 KiB
Python

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