1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
|
from typing import List, Optional
from cryptography.hazmat.backends import default_backend
from cryptography.x509 import load_der_x509_certificate
from OpenSSL.crypto import X509, X509Store, X509StoreContext, X509StoreContextError
from .exceptions import InvalidCertificateChain
from .pem_cert_bytes_to_open_ssl_x509 import pem_cert_bytes_to_open_ssl_x509
def validate_certificate_chain(
*,
x5c: List[bytes],
pem_root_certs_bytes: Optional[List[bytes]] = None,
) -> bool:
"""Validate that the certificates in x5c chain back to a known root certificate
Args:
`x5c`: X5C certificates from a registration response's attestation statement
(optional) `pem_root_certs_bytes`: Any additional (PEM-formatted)
root certificates that may complete the certificate chain
Raises:
`helpers.exceptions.InvalidCertificateChain` if chain cannot be validated
"""
if pem_root_certs_bytes is None or len(pem_root_certs_bytes) < 1:
# We have no root certs to chain back to, so just pass on validation
return True
# Make sure we have at least one certificate to try and link back to a root cert
if len(x5c) < 1:
raise InvalidCertificateChain("x5c was empty")
# Prepare leaf cert
try:
leaf_cert_bytes = x5c[0]
leaf_cert_crypto = load_der_x509_certificate(leaf_cert_bytes, default_backend())
leaf_cert = X509().from_cryptography(leaf_cert_crypto)
except Exception as err:
raise InvalidCertificateChain(f"Could not prepare leaf cert: {err}")
# Prepare any intermediate certs
try:
# May be an empty array, that's fine
intermediate_certs_bytes = x5c[1:]
intermediate_certs_crypto = [
load_der_x509_certificate(cert, default_backend()) for cert in intermediate_certs_bytes
]
intermediate_certs = [X509().from_cryptography(cert) for cert in intermediate_certs_crypto]
except Exception as err:
raise InvalidCertificateChain(f"Could not prepare intermediate certs: {err}")
# Prepare a collection of possible root certificates
root_certs_store = X509Store()
try:
for cert in pem_root_certs_bytes:
root_certs_store.add_cert(pem_cert_bytes_to_open_ssl_x509(cert))
except Exception as err:
raise InvalidCertificateChain(f"Could not prepare root certs: {err}")
# Load certs into a "context" for validation
context = X509StoreContext(
store=root_certs_store,
certificate=leaf_cert,
chain=intermediate_certs,
)
# Validate the chain (will raise if it can't)
try:
context.verify_certificate()
except X509StoreContextError:
raise InvalidCertificateChain("Certificate chain could not be validated")
return True
|