SignXML: XML Signature in Python
================================

*SignXML* is an implementation of the W3C `XML Signature <http://en.wikipedia.org/wiki/XML_Signature>`_ standard in
Python. This standard (also known as XMLDSig and `RFC 3275 <http://www.ietf.org/rfc/rfc3275.txt>`_) is used to provide
payload security in `SAML 2.0 <http://en.wikipedia.org/wiki/SAML_2.0>`_ and
`WS-Security <https://en.wikipedia.org/wiki/WS-Security>`_, among other uses.  Three versions of the
standard exist (`Version 1 "Second Edition" <http://www.w3.org/TR/xmldsig-core/>`_, `Version 1.1
<http://www.w3.org/TR/xmldsig-core1/>`_, and `Version 2.0 <http://www.w3.org/TR/xmldsig-core2>`_). *SignXML* implements
all of the required components of the standard, and most recommended ones. Its features are:

* Use of `defusedxml.lxml <https://bitbucket.org/tiran/defusedxml>`_ to defend against common XML-based attacks when
  verifying signatures
* Extensions to allow signing with and verifying X.509 certificate chains, including hostname/CN validation
* Support for exclusive XML canonicalization with inclusive prefixes (`InclusiveNamespaces PrefixList
  <http://www.w3.org/TR/xml-exc-c14n/#def-InclusiveNamespaces-PrefixList>`_, required to verify signatures generated by
  some SAML implementations)
* Modern Python compatibility (2.7-3.5+ and PyPy)
* Well-supported, portable, reliable dependencies: `lxml <https://github.com/lxml/lxml>`_, `defusedxml
  <https://bitbucket.org/tiran/defusedxml>`_, `cryptography <https://github.com/pyca/cryptography>`_, `eight
  <https://github.com/kislyuk/eight>`_, `pyOpenSSL <https://github.com/pyca/pyopenssl>`_
* Comprehensive testing (including the XMLDSig interoperability suite) and `continuous integration
  <https://travis-ci.org/XML-Security/signxml>`_
* Simple interface with useful defaults
* Compactness, readability, and extensibility

Installation
------------
::

    pip install signxml

Note: SignXML depends on `lxml <https://github.com/lxml/lxml>`_ and `cryptography
<https://github.com/pyca/cryptography>`_, which in turn depend on `OpenSSL <https://www.openssl.org/>`_, `LibXML
<http://xmlsoft.org/>`_, and Python tools to interface with them. You can install those as follows:

+--------------+---------+---------------------------------------------------------------------------------------------+
| OS           | Python  | Command                                                                                     |
+==============+=========+=============================================================================================+
| Ubuntu 16.04 | Python 2| apt-get install python-dev python-cffi libxml2-dev libxslt1-dev libssl-dev                  |
|              |         | python-lxml python-cryptography python-openssl python-certifi python-defusedxml             |
+--------------+---------+---------------------------------------------------------------------------------------------+
| Ubuntu 16.04 | Python 3| apt-get install python3-dev python3-cffi libxml2-dev libxslt1-dev libssl-dev                |
|              |         | python3-lxml python3-cryptography python3-openssl python3-certifi python3-defusedxml        |
+--------------+---------+---------------------------------------------------------------------------------------------+
| Ubuntu 14.04 | Python 2| apt-get install python-dev python-cffi libxml2-dev libxslt1-dev libssl-dev                  |
+--------------+---------+---------------------------------------------------------------------------------------------+
| Ubuntu 14.04 | Python 3| apt-get install python3-dev python3-cffi libxml2-dev libxslt1-dev libssl-dev                |
+--------------+---------+---------------------------------------------------------------------------------------------+
| Ubuntu 12.04 | Python 2| apt-get install python-dev libxml2-dev libxslt1-dev libssl-dev; pip install cffi            |
+--------------+---------+---------------------------------------------------------------------------------------------+
| Red Hat      | Python 2| yum install python-devel python-cffi libxml2-devel libxslt1-devel openssl-devel             |
+--------------+---------+---------------------------------------------------------------------------------------------+
| Red Hat      | Python 3| yum install python3-devel python3-cffi libxml2-devel libxslt1-devel openssl-devel           |
+--------------+---------+---------------------------------------------------------------------------------------------+
| OS X/Homebrew|         | xcode-select --install; brew install openssl;                                               |
|              |         | export LDFLAGS="-L$(brew --prefix openssl)/lib" CFLAGS="-I$(brew --prefix openssl)/include" |
+--------------+---------+---------------------------------------------------------------------------------------------+

Synopsis
--------

SignXML uses the ElementTree API (also supported by lxml) to work with XML data.

.. code-block:: python

    from signxml import XMLSigner, XMLVerifier

    cert = open("example.pem").read()
    key = open("example.key").read()
    root = ElementTree.fromstring(signature_data)
    signed_root = XMLSigner().sign(root, key=key, cert=cert)
    verified_data = XMLVerifier().verify(signed_root).signed_xml

.. _verifying-saml-assertions:

Verifying SAML assertions
~~~~~~~~~~~~~~~~~~~~~~~~~

Assuming ``metadata.xml`` contains SAML metadata for the assertion source:

.. code-block:: python

    from lxml import etree
    from base64 import b64decode
    from signxml import XMLVerifier

    with open("metadata.xml", "rb") as fh:
        cert = etree.parse(fh).find("//ds:X509Certificate").text

    assertion_data = XMLVerifier().verify(b64decode(assertion_body), x509_cert=cert).signed_xml

.. admonition:: Signing SAML assertions

 The SAML assertion schema specifies a location for the enveloped XML signature (between ``<Issuer>`` and
 ``<Subject>``). To sign a SAML assertion in a schema-compliant way, insert a signature placeholder tag at that location
 before calling XMLSigner: ``<ds:Signature Id="placeholder"></ds:Signature>``.

.. admonition:: See what is signed

 It is important to understand and follow the best practice rule of "See what is signed" when verifying XML
 signatures. The gist of this rule is: if your application neglects to verify that the information it trusts is
 what was actually signed, the attacker can supply a valid signature but point you to malicious data that wasn't signed
 by that signature. Failure to follow this rule can lead to vulnerability against attacks like
 `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.

.. admonition:: Establish trust

 If you do not supply any keyword arguments to ``verify()``, the default behavior is to trust **any** valid XML
 signature generated using a valid X.509 certificate trusted by your system's CA store. This means anyone can
 get an SSL certificate and generate a signature that you will trust. To establish trust in the signer, use the
 ``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.

 **Recommended reading:** `W3C XML Signature Best Practices for Applications <http://www.w3.org/TR/xmldsig-bestpractices/#practices-applications>`_, `OWASP: On Breaking SAML: Be Whoever You Want to Be <https://www.owasp.org/images/2/28/Breaking_SAML_Be_Whoever_You_Want_to_Be_-_Juraj_Somorovsky%2BChristian_Mainka.pdf>`_

XML signature methods: enveloped, detached, enveloping
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The XML Signature specification defines three ways to compose a signature with the data being signed: enveloped,
detached, and enveloping signature. Enveloped is the default method. To specify the type of signature that you want to
generate, pass the ``method`` argument to ``sign()``:

.. code-block:: python

    signed_root = XMLSigner(method=signxml.methods.detached).sign(root, key=key, cert=cert)
    verified_data = XMLVerifier().verify(signed_root).signed_xml

For detached signatures, the code above will use the ``Id`` or ``ID`` attribute of ``root`` to generate a relative URI
(``<Reference URI="#value"``). You can also override the value of ``URI`` by passing a ``reference_uri`` argument to
``sign()``. To verify a detached signature that refers to an external entity, pass a callable resolver in
``XMLVerifier().verify(data, uri_resolver=...)``.

See the `API documentation <https://signxml.readthedocs.io/en/latest/#id3>`_ for more.

Authors
-------
* Andrey Kislyuk

Links
-----
* `Project home page (GitHub) <https://github.com/XML-Security/signxml>`_
* `Documentation (Read the Docs) <https://signxml.readthedocs.io/en/latest/>`_
* `Package distribution (PyPI) <https://pypi.python.org/pypi/signxml>`_
* `Change log <https://github.com/XML-Security/signxml/blob/master/Changes.rst>`_
* `List of W3C XML Signature standards and drafts <http://www.w3.org/TR/#tr_XML_Signature>`_
* `W3C Recommendation: XML Signature Syntax and Processing (Second Edition) <http://www.w3.org/TR/xmldsig-core/>`_
* `W3C Recommendation: XML Signature Syntax and Processing Version 1.1 <http://www.w3.org/TR/xmldsig-core1>`_
* `W3C Working Group Note: XML Signature Syntax and Processing Version 2.0 <http://www.w3.org/TR/xmldsig-core2>`_
* `W3C Working Group Note: XML Security 2.0 Requirements and Design Considerations <https://www.w3.org/TR/2013/NOTE-xmlsec-reqs2-20130411/>`_
* `W3C Working Group Note: XML Signature Best Practices <http://www.w3.org/TR/xmldsig-bestpractices/>`_
* `XML-Signature Interoperability <http://www.w3.org/Signature/2001/04/05-xmldsig-interop.html>`_
* `W3C Working Group Note: Test Cases for C14N 1.1 and XMLDSig Interoperability <http://www.w3.org/TR/xmldsig2ed-tests/>`_
* `XMLSec: Related links <https://www.aleksey.com/xmlsec/related.html>`_
* `OWASP SAML Security Cheat Sheet <https://www.owasp.org/index.php/SAML_Security_Cheat_Sheet>`_

Bugs
~~~~
Please report bugs, issues, feature requests, etc. on `GitHub <https://github.com/XML-Security/signxml/issues>`_.

License
-------
Licensed under the terms of the `Apache License, Version 2.0 <http://www.apache.org/licenses/LICENSE-2.0>`_.

.. image:: https://img.shields.io/travis/XML-Security/signxml.svg
        :target: https://travis-ci.org/XML-Security/signxml
.. image:: https://codecov.io/github/XML-Security/signxml/coverage.svg?branch=master
        :target: https://codecov.io/github/XML-Security/signxml?branch=master
.. image:: https://img.shields.io/pypi/v/signxml.svg
        :target: https://pypi.python.org/pypi/signxml
.. image:: https://img.shields.io/pypi/l/signxml.svg
        :target: https://pypi.python.org/pypi/signxml
.. image:: https://readthedocs.org/projects/signxml/badge/?version=latest
        :target: https://signxml.readthedocs.io/


