Skip to content

Commit

Permalink
Fully remove the ca_path parameter; add docs for signature location p…
Browse files Browse the repository at this point in the history
…inning
  • Loading branch information
kislyuk committed Sep 20, 2024
1 parent 5fbc7df commit 7e7f504
Show file tree
Hide file tree
Showing 4 changed files with 26 additions and 21 deletions.
12 changes: 9 additions & 3 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,14 @@ Assuming ``metadata.xml`` contains SAML metadata for the assertion source:
`SAML signature wrapping <https://www.usenix.org/system/files/conference/usenixsecurity12/sec12-final91.pdf>`_.

In SignXML, you can ensure that the information signed is what you expect to be signed by only trusting the
data returned by the ``verify()`` method. The ``signed_xml`` attribute of the return value is the XML node or string that
was signed.
data returned by ``XMLVerifier.verify()``. The ``signed_xml`` attribute of the return value is the XML node or string
that was signed. We also recommend that you assert the expected location for the signature within the document:

.. code-block:: python
from signxml import XMLVerifier, SignatureConfiguration
config = SignatureConfiguration(location="./{urn:oasis:names:tc:SAML:2.0:assertion}Assertion")
XMLVerifier(...).verify(..., expect_config=config)
**Recommended reading:** `W3C XML Signature Best Practices for Applications
<http://www.w3.org/TR/xmldsig-bestpractices/#practices-applications>`_, `On Breaking SAML: Be Whoever You Want to Be
Expand All @@ -106,7 +112,7 @@ Assuming ``metadata.xml`` contains SAML metadata for the assertion source:
``x509_cert`` argument to specify a certificate that was pre-shared out-of-band (e.g. via SAML metadata, as
shown in *Verifying SAML assertions*), or ``cert_subject_name`` to specify a
subject name that must be in the signing X.509 certificate given by the signature (verified as if it were a
domain name), or ``ca_pem_file``/``ca_path`` to give a custom CA.
domain name), or ``ca_pem_file`` to give a custom CA.

XML signature construction methods: enveloped, detached, enveloping
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down
6 changes: 1 addition & 5 deletions signxml/util/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,14 +225,10 @@ class X509CertChainVerifier:
contact SignXML maintainers.
"""

def __init__(self, ca_pem_file=None, ca_path=None, verification_time=None):
def __init__(self, ca_pem_file=None, verification_time=None):
if ca_pem_file is None:
ca_pem_file = certifi.where()
self.ca_pem_file = ca_pem_file
if ca_path is not None:
msg = "CApath is not supported. If you need this feature, please contact SignXML maintainers."
raise NotImplementedError(msg)

self.verification_time = verification_time

@property
Expand Down
25 changes: 14 additions & 11 deletions signxml/verifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -233,8 +233,8 @@ def _apply_transforms(self, payload, *, transforms_node: etree._Element, signatu

return payload

def get_cert_chain_verifier(self, ca_pem_file, ca_path):
return X509CertChainVerifier(ca_pem_file=ca_pem_file, ca_path=ca_path)
def get_cert_chain_verifier(self, ca_pem_file):
return X509CertChainVerifier(ca_pem_file=ca_pem_file)

def _match_key_values(self, key_value, der_encoded_key_value, signing_cert, signature_alg):
if self.config.ignore_ambiguous_key_info is False:
Expand Down Expand Up @@ -279,7 +279,6 @@ def verify(
cert_subject_name: Optional[str] = None,
cert_resolver: Optional[Callable] = None,
ca_pem_file: Optional[Union[str, bytes]] = None,
ca_path: Optional[str] = None,
hmac_key: Optional[str] = None,
validate_schema: bool = True,
parser=None,
Expand All @@ -302,8 +301,16 @@ def verify(
signed by that signature.
In SignXML, you can ensure that the information signed is what you expect to be signed by only trusting the
data returned by the ``verify()`` method. The return value is the XML node or string that was signed. Also,
depending on the canonicalization method used by the signature, comments in the XML data may not be subject to
data returned by ``XMLVerifier.verify()``. The ``signed_xml`` attribute of the return value is the XML node or string
that was signed. We also recommend that you assert the expected location for the signature within the document:
.. code-block:: python
from signxml import XMLVerifier, SignatureConfiguration
config = SignatureConfiguration(location="./{urn:oasis:names:tc:SAML:2.0:assertion}Assertion")
XMLVerifier(...).verify(..., expect_config=config)
Depending on the canonicalization method used by the signature, comments in the XML data may not be subject to
signing, so may need to be untrusted. If so, they are excised from the return value of ``verify()``.
**Recommended reading:** http://www.w3.org/TR/xmldsig-bestpractices/#practices-applications
Expand All @@ -316,7 +323,7 @@ def verify(
``x509_cert`` argument to specify a certificate that was pre-shared out-of-band (e.g. via SAML metadata, as
shown in :ref:`Verifying SAML assertions <verifying-saml-assertions>`), or ``cert_subject_name`` to specify a
subject name that must be in the signing X.509 certificate given by the signature (verified as if it were a
domain name), or ``ca_pem_file``/``ca_path`` to give a custom CA.
domain name), or ``ca_pem_file`` to give a custom CA.
:param data: Signature data to verify
:type data: String, file-like object, or XML ElementTree Element API compatible object
Expand All @@ -336,10 +343,6 @@ def verify(
:param ca_pem_file:
Filename of a PEM file containing certificate authority information to use when verifying certificate-based
signatures.
:param ca_path:
Path to a directory containing PEM-formatted certificate authority files to use when verifying
certificate-based signatures. If neither **ca_pem_file** nor **ca_path** is given, the Mozilla CA bundle
provided by :py:mod:`certifi` will be loaded.
:param hmac_key: If using HMAC, a string containing the shared secret.
:param validate_schema: Whether to validate **data** against the XML Signature schema.
:param parser:
Expand Down Expand Up @@ -433,7 +436,7 @@ def verify(
else:
cert_chain = [x509.load_pem_x509_certificate(add_pem_header(cert)) for cert in certs]

cert_verifier = self.get_cert_chain_verifier(ca_pem_file=ca_pem_file, ca_path=ca_path)
cert_verifier = self.get_cert_chain_verifier(ca_pem_file=ca_pem_file)

signing_cert = cert_verifier.verify(cert_chain)
elif isinstance(self.x509_cert, x509.Certificate):
Expand Down
4 changes: 2 additions & 2 deletions test/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ def reset_tree(t, method):

def get_verifier_for_year(year: int):
class _Verifier(XMLVerifier):
def get_cert_chain_verifier(self, ca_pem_file, ca_path):
verifier = super().get_cert_chain_verifier(ca_pem_file, ca_path)
def get_cert_chain_verifier(self, ca_pem_file):
verifier = super().get_cert_chain_verifier(ca_pem_file)
verifier.verification_time = datetime(year, 1, 1)
return verifier

Expand Down

0 comments on commit 7e7f504

Please sign in to comment.