|
12 | 12 | import copy |
13 | 13 | import traceback |
14 | 14 | from lxml.builder import ElementMaker |
| 15 | +from cryptography.x509 import ExtensionNotFound, BasicConstraints, load_pem_x509_certificate |
15 | 16 | from xmlsec.exceptions import XMLSigException |
16 | 17 | from xmlsec import constants |
17 | 18 | from xmlsec.utils import parse_xml, pem2b64, unescape_xml_entities, delete_elt, root_elt, b64d, b64e, etree_to_string |
@@ -313,6 +314,27 @@ def _verify(t, keyspec, sig_path=".//{%s}Signature" % NS['ds'], drop_signature=F |
313 | 314 | this_cert = xmlsec.crypto.from_keyspec(keyspec, signature_element=sig) |
314 | 315 | log.debug("key size: {!s} bits".format(this_cert.keysize)) |
315 | 316 |
|
| 317 | + # Try verification by CA signed signing certificate |
| 318 | + bc = None |
| 319 | + try: |
| 320 | + bc = this_cert.key.extensions.get_extension_for_class(BasicConstraints) |
| 321 | + except ExtensionNotFound: |
| 322 | + pass |
| 323 | + else: |
| 324 | + # If this_cert a CA cert it is probably not the signing cert |
| 325 | + if bc.value.ca is True: |
| 326 | + # Find X509Certificate in signature that is child of the root element |
| 327 | + cert = t.find("/ds:Signature/ds:KeyInfo/ds:X509Data/ds:X509Certificate", namespaces=NS) |
| 328 | + if cert is not None: |
| 329 | + certspec = "-----BEGIN CERTIFICATE-----\n" + cert.text.strip() + "\n-----END CERTIFICATE-----" |
| 330 | + embedded_cert = load_pem_x509_certificate(certspec.encode()) |
| 331 | + try: |
| 332 | + embedded_cert.verify_directly_issued_by(this_cert.key) |
| 333 | + except Exception: |
| 334 | + raise XMLSigException("Metadata certificate not signed by CA") |
| 335 | + else: |
| 336 | + this_cert.key = embedded_cert |
| 337 | + |
316 | 338 | si = sig.find(".//{%s}SignedInfo" % NS['ds']) |
317 | 339 | log.debug("Found signedinfo {!s}".format(etree.tostring(si))) |
318 | 340 | cm_alg = _cm_alg(si) |
|
0 commit comments