Giter VIP home page Giter VIP logo

signxml's Introduction

SignXML: XML Signature and XAdES in Python

SignXML is an implementation of the W3C XML Signature standard in Python. This standard (also known as "XMLDSig") is used to provide payload security in SAML 2.0, XAdES, and WS-Security, among other uses. The standard is defined in the W3C Recommendation XML Signature Syntax and Processing Version 1.1. SignXML implements all of the required components of the Version 1.1 standard, and most recommended ones. Its features are:

  • Use of a libxml2-based XML parser configured to defend against common XML attacks when verifying signatures
  • Extensions to allow signing with and verifying X.509 certificate chains, including hostname/CN validation
  • Extensions to sign and verify XAdES signatures
  • Support for exclusive XML canonicalization with inclusive prefixes (InclusiveNamespaces PrefixList, required to verify signatures generated by some SAML implementations)
  • Modern Python compatibility (3.7-3.11+ and PyPy)
  • Well-supported, portable, reliable dependencies: lxml, cryptography, pyOpenSSL
  • Comprehensive testing (including the XMLDSig interoperability suite) and continuous integration
  • Simple interface with useful, ergonomic, and secure defaults (no network calls, XSLT or XPath transforms)
  • Compactness, readability, and extensibility

Installation

pip install signxml

Note: SignXML depends on lxml and cryptography, which in turn depend on OpenSSL, LibXML, and Python tools to interface with them. You can install those as follows:

OS Command
Ubuntu apt-get install --no-install-recommends python3-pip python3-wheel python3-setuptools python3-openssl python3-lxml
Red Hat, Amazon Linux, CentOS yum install python3-pip python3-pyOpenSSL python3-lxml
Mac OS Install Homebrew, then run brew install python.

Synopsis

SignXML uses the lxml ElementTree API to work with XML data.

from lxml import etree
from signxml import XMLSigner, XMLVerifier

data_to_sign = "<Test/>"
cert = open("cert.pem").read()
key = open("privkey.pem").read()
root = etree.fromstring(data_to_sign)
signed_root = XMLSigner().sign(root, key=key, cert=cert)
verified_data = XMLVerifier().verify(signed_root).signed_xml

To make this example self-sufficient for test purposes:

  • Generate a test certificate and key using openssl req -x509 -nodes -subj "/CN=test" -days 1 -newkey rsa -keyout privkey.pem -out cert.pem (run yum install openssl on Red Hat).
  • Pass the x509_cert=cert keyword argument to XMLVerifier.verify(). (In production, ensure this is replaced with the correct configuration for the trusted CA or certificate - this determines which signatures your application trusts.)

Verifying SAML assertions

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

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

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

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.

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.

Recommended reading: W3C XML Signature Best Practices for Applications, On Breaking SAML: Be Whoever You Want to Be, Duo Finds SAML Vulnerabilities Affecting Multiple Implementations

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.

XML signature construction 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():

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 for more details.

XML representation details: Configuring namespace prefixes and whitespace

Some applications require a particular namespace prefix configuration - for example, a number of applications assume that the http://www.w3.org/2000/09/xmldsig# namespace is set as the default, unprefixed namespace instead of using the customary ds: prefix. While in normal use namespace prefix naming is an insignificant representation detail, it can be significant in some XML canonicalization and signature configurations. To configure the namespace prefix map when generating a signature, set the XMLSigner.namespaces attribute:

signer = signxml.XMLSigner(...)
signer.namespaces = {None: signxml.namespaces.ds}
signed_root = signer.sign(...)

Similarly, whitespace in the signed document is significant for XML canonicalization and signature purposes. Do not pretty-print the XML after generating the signature, since this can unfortunately render the signature invalid.

XML parsing security and compatibility with xml.etree.ElementTree

SignXML uses the lxml ElementTree library, not the ElementTree from Python's standard library, to work with XML. lxml is used due to its superior resistance to XML attacks, as well as XML canonicalization and namespace organization features. It is recommended that you pass XML string input directly to signxml before further parsing, and use lxml to work with untrusted XML input in general. If you do pass xml.etree.ElementTree objects to SignXML, you should be aware of differences in XML namespace handling between the two libraries. See the following references for more information:

XAdES signatures

XAdES ("XML Advanced Electronic Signatures") is a standard for attaching metadata to XML Signature objects. This standard is endorsed by the European Union as the implementation for its eSignature regulations.

SignXML supports signing and verifying documents using XAdES signatures:

from signxml import DigestAlgorithm
from signxml.xades import (XAdESSigner, XAdESVerifier, XAdESVerifyResult,
                           XAdESSignaturePolicy, XAdESDataObjectFormat)
signature_policy = XAdESSignaturePolicy(
    Identifier="MyPolicyIdentifier",
    Description="Hello XAdES",
    DigestMethod=DigestAlgorithm.SHA256,
    DigestValue="Ohixl6upD6av8N7pEvDABhEL6hM=",
)
data_object_format = XAdESDataObjectFormat(
    Description="My XAdES signature",
    MimeType="text/xml",
)
signer = XAdESSigner(
    signature_policy=signature_policy,
    claimed_roles=["signer"],
    data_object_format=data_object_format,
    c14n_algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315",
)
signed_doc = signer.sign(doc, key=private_key, cert=certificate)
verifier = XAdESVerifier()
verify_results = verifier.verify(
    signed_doc, x509_cert=certificate, expect_references=3, expect_signature_policy=signature_policy
)
for verify_result in verify_results:
    if isinstance(verify_result, XAdESVerifyResult):
        verify_result.signed_properties  # use this to access parsed XAdES properties

Authors

Links

Bugs

Please report bugs, issues, feature requests, etc. on GitHub.

Versioning

This package follows the Semantic Versioning 2.0.0 standard. To control changes, it is recommended that application developers pin the package version and manage it using pip-tools or similar. For library developers, pinning the major version is recommended.

License

Copyright 2014-2023, Andrey Kislyuk and SignXML contributors. Licensed under the terms of the Apache License, Version 2.0. Distribution of the LICENSE and NOTICE files with source copies of this package and derivative works is REQUIRED as specified by the Apache License.

https://codecov.io/github/XML-Security/signxml/coverage.svg?branch=master

signxml's People

Contributors

adamchainz avatar adschellevis avatar agronholm avatar arthurdejong avatar bobdoah avatar bvanelli avatar codeinthehole avatar jhominal avatar kislyuk avatar klondi avatar lscorcia avatar mbarrien avatar msetina avatar nagylzs avatar perltrustly avatar pokoli avatar rjpercival avatar ryandub avatar schlenk avatar soby avatar stan-janssen avatar viclevy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

signxml's Issues

Signature nodes should be removed from the returned payload on verify()

When doing a verify() the payload returned contains the signature nodes. As these nodes have to be removed before generating the hash (At least for enveloped signatures) this means that any nodes inserted on the signature might be considered valid despite not being so which opens the risk for accepting unsigned data as valid.

I can submit a patch to this issue later if #46 is merged.

No module _winreg

Running cherrypy web app on Ubuntu 14.04.
from signxml import xmldsig
causes the following error when I start up my app:

[08/May/2015:14:56:06] ENGINE Bus STARTED
[08/May/2015:14:56:07] ENGINE Error in background task thread function Autoreloader.run of <cherrypy.process.plugins.Autoreloader object at 0x7f3f498477d0>>.
Traceback (most recent call last):
File "/usr/local/lib/python2.7/dist-packages/cherrypy/process/plugins.py", line 500, in run
self.function(_self.args, *_self.kwargs)
File "/usr/local/lib/python2.7/dist-packages/cherrypy/process/plugins.py", line 632, in run
for filename in self.sysfiles() | self.files:
File "/usr/local/lib/python2.7/dist-packages/cherrypy/process/plugins.py", line 621, in sysfiles
f = getattr(m, 'file', None)
File "/usr/local/lib/python2.7/dist-packages/eight/utils.py", line 41, in getattr
self._module = import(self._name)
ImportError: No module named _winreg`

X509Certificate filled not according to spec

Currently the ds:X509Certificate element gets filled from OpenSSL.crypto.dump_certificate(), but the problem here is that it also adds headers and footers like -----BEGIN PUBLIC KEY-----. This is not allowed by the XMLDSig specification. Reading e.g. http://www.w3.org/TR/2002/REC-xmldsig-core-20020212/#sec-X509Data or http://www.w3.org/TR/xmldsig-core2/#sec-X509Data you note that:

  • The X509Certificate element, which contains a base64-encoded [X509v3] certificate
  • The schema has: <element name="X509Certificate" type="base64Binary"/>
  • All the examples start immediately with MII...
  • base64Binary disallows the -

Relevant line is 399 in __init__.py. Looking at how to resolve this.

xml.etree.ElementTree nodes

The change made for issue #19 may not be 100% yet. There's a slight difference in behaviour: using sign() on an lxml etree node modifies it in-place, whereas using it on a stdlib etree node returns a new signed copy but doesn't modify the original.
I don't see the docs specifically make a promise to sign in-place, but the very first example (signing and then verifying) relies on it.

To build from my previous test case:

x = "<foo><bar></bar></foo>"
root1=xml.etree.ElementTree.fromstring(x)
root2=lxml.etree.fromstring(x)

signxml.xmldsig(root1).sign(key=key, cert=cert)
signxml.xmldsig(root2).sign(key=key, cert=cert)

>>>signxml.xmldsig(root1).verify()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python2.7/dist-packages/signxml/__init__.py", line 580, in verify
    signature = self._find(root, "Signature")
  File "/usr/local/lib/python2.7/dist-packages/signxml/__init__.py", line 663, in _find
    raise InvalidInput("Expected to find XML element {} in {}".format(query, element.tag))
signxml.InvalidInput: Expected to find XML element Signature in foo
#no good! 

>>>signxml.xmldsig(root2).verify()
<Element foo at 0x7fb4d8c9ca28>
#okay!


#But....
root1 = signxml.xmldsig(root1).sign(key=key, cert=cert)
>>>signxml.xmldsig(root1).verify()
<Element foo at 0x7fb4d8ca0560>
#this actually works!

Cannot verify signatures

I am trying to verify the signature of two XML documents.

When I try to verify an example XML document from the SII (Chile’s IRS), I get:

Traceback (most recent call last):
  File "test_verify.py", line 30, in <module>
    verifier.verify(xml_tree, x509_cert=cert)
  File "/usr/local/lib/python2.7/dist-packages/signxml/__init__.py", line 666, in verify
    raise InvalidSignature("Signature verification failed: {}".format(reason))
InvalidSignature: Signature verification failed: block type is not 01

When I try to verify an XML document generated and signed by myself using signxml, I get:

Traceback (most recent call last):
  File "test_verify.py", line 30, in <module>
    verifier.verify(xml_tree, x509_cert=cert)
  File "/usr/local/lib/python2.7/dist-packages/signxml/__init__.py", line 666, in verify
    raise InvalidSignature("Signature verification failed: {}".format(reason))
InvalidSignature: Signature verification failed: bad signature

This is the script I am using:

# coding=utf-8

from tempfile import TemporaryFile
from traceback import print_exc
from urllib2 import urlopen
from zipfile import ZipFile

from lxml.etree import parse

from signxml import XMLVerifier


verifier = XMLVerifier()

temporary_file = TemporaryFile()
remote_file = urlopen('http://www.sii.cl/factura_electronica/ejemplo_xml.zip')
temporary_file.write(remote_file.read())
zip_file = ZipFile(temporary_file)
xml_file = zip_file.open('F60T33-ejemplo.xml')
xml_tree_1 = parse(xml_file)

xml_tree_2 = parse(urlopen('http://pastebin.com/raw/1jCkqCUX'))

for xml_tree in (xml_tree_1, xml_tree_2):
    try:
        cert = xml_tree.find(
            './/{http://www.w3.org/2000/09/xmldsig#}X509Certificate').text
        cert = '-----BEGIN CERTIFICATE-----\n{}' \
               '\n-----END CERTIFICATE-----\n'.format(cert.strip('\n'))
        verifier.verify(xml_tree, x509_cert=cert)
        print(u"Result: SUCCESS")
    except:
        print_exc()
        print(u"Result: FAILURE")
    print('')

Any idea of what I am doing wrong, or how to find out?

ADFS Assertion Compatibility

The ADFS server I am working with is sending a reference URI like "#_639146ac-a51f-44a3-8a2d-c2c4d944c7d3". However, the element it is referring to is identified by an "AssertionID" attribute, instead of just "ID".

Here is the workaround I'm using right now:

from signxml import xmldsig, InvalidInput

class ADFSAssertionDSig(xmldsig):

    def _resolve_reference(self, doc_root, reference, uri_resolver=None):
        try:
            return xmldsig._resolve_reference(self, doc_root, reference, uri_resolver)
        except InvalidInput:
            return self._resolve_reference_by_assertion_id_attribute(doc_root, reference, uri_resolver)

    def _resolve_reference_by_assertion_id_attribute(self, doc_root, reference, uri_resolver=None):
        uri = reference.get("URI")
        results = doc_root.xpath("..//*[@AssertionId=$uri]", uri=uri.lstrip("#"))
        if len(results) < 1:
            results += doc_root.xpath("..//*[@AssertionID=$uri]", uri=uri.lstrip("#"))
        if len(results) < 1:
            raise InvalidInput("Unable to resolve reference URI: {}".format(uri))
        return results[0]

I'm thinking there should be a more elegant solution.

BTW, I thanks for the Python 3 fixes. I was actually working on a pull request last weekend for that, only to see on Monday that you had got it done.

Enveloped signature: Signature-placeholder not found when not direct child of root

Hello,

According to then W3C specification an enveloped signature is

Signature, Enveloped
The signature is over the XML content that contains the signature as an element. The content provides the root XML document element. Obviously, enveloped signatures must take care not to include their own value in the calculation of the SignatureValue.

There is no need for the signature element to be a direct child of the root element.

So you may use

<TheInformation>
    <SomeInfo>
        ...
        <Security>
            ...
            <Signature>  
                ...
            </Signature>  
            ...
        </Security>
        ...
    <\SomeInfo>
    <SomeOtherInfo>
        ...
    <\SomeOtherInfo>
    ...
</TheInformation>

And there are many implementations which put the signature element somewhere in the
XML-tree.

Your implementation (method _getPayload_c14n) uses

.
.
signature_placeholders = self._findall(self.data, "Signature[@Id='placeholder']")
.
.

to find the position of the signature element and _findall is defined as

def _findall(self, element, query, namespace="ds"):
    return element.findall(namespace + ":" + query, namespaces=namespaces)

But the element-method findall only searches for direct childs if called with a simple name

element.findall('ds:Signature')

To find all descendants you have to search with an x-path expression beginning with './/'

element.findall('.//ds:Signature')

I changed the definition of _findall to

def _findall(self, element, query, namespace="ds"):
    return element.findall('.//' + namespace + ":" + query, namespaces=namespaces)

and now the Program behaves correctly.

But since you use your _findall method in several places, I don't know if I destroyed the other uses of this routine.
And until now I didn't find the time to analyze, if the non-recursive search occurs in other places of your program (I think your verify function does also not work for the above example).

regards Volker

xml:id isn't accepted as a valid id_attribute

If I have a signed document, with the proper reference element as:

 <Reference URI="#ref846526240431F74B">

And a matching element in the document:

<credential xml:id="ref846526240431F74B">

This isn't found, because xml:id isn't a valid id_attribute searched by _resolve_reference:

Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/usr/local/lib/python2.7/dist-packages/signxml/__init__.py", line 636, in verify
    payload = self._resolve_reference(root, reference, uri_resolver=uri_resolver)
  File "/usr/local/lib/python2.7/dist-packages/signxml/__init__.py", line 498, in _resolve_reference
    raise InvalidInput("Unable to resolve reference URI: {}".format(uri))
InvalidInput: Unable to resolve reference URI: #ref846526240431F74B

signxml.InvalidCertificate: [20, 0, 'unable to get local issuer certificate']

I'm tying to add xml signature to a XML file
but don't seem to validate the it.
What am I doing wrong ?
Am I parsing the xml file incorrectly ?

Here is the python code:

from signxml import xmldsig
import xml.etree.ElementTree as ET
cert = open("x.pem").read()
key = open("x.key").read()
tree = ET.parse('alert2.xml')
root = tree.getroot()
signed_root = xmldsig(root).sign(key=key, cert=cert)
verified_data = xmldsig(signed_root).verify()

The xml file is attached just remove the .txt at the end
alert2.xml.txt

Verify signature using sha256 and Transform

First off, this looks really promising for me, but verifying my signature yields this error:

Traceback (most recent call last):
File "islykill.py", line 186, in parse_saml
k = xmldsig(dec_resp).verify()
File "c:\users\sindri\tmp\signxml\signxml__init__.py", line 261, in verify
raise InvalidInput("Expected DigestMethod#Algorithm to start with "+XMLDSIG_NS)
InvalidInput: Expected DigestMethod#Algorithm to start with http://www.w3.org/2000/09/xmldsig#

My signature looks like this:

<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
    <SignedInfo>
        <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
        <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
        <Reference URI="#_44bf7ba9-9337-4ae1-8e70-a737dcb585cc">
            <Transforms>
                <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
                <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
            </Transforms>
            <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
            <DigestValue>[redacted]</DigestValue>
        </Reference>
    </SignedInfo>
    <SignatureValue>[redacted]</SignatureValue>
    <KeyInfo>
        <X509Data>
            <X509Certificate>[redacted]</X509Certificate>
        </X509Data>
    </KeyInfo>
</Signature>

In signxml/__init__.py:260 it checks if the Algorithm attribute of DigestMethod starts with "http://www.w3.org/2000/09/xmldsig#". If I understand correctly, the transforms should be applied before verifying the signature. This is where my knowledge of XML signatures comes to a halt. Would these two transforms change the DigestMethod Algorithm? Or is sha256 support something that signxml hasn't taken into account?

Exception Value: Invalid input object: Element

Dear all,

I am trying to validate an xml signature (saml token) using signxml.
I am using the following code:

cert=open("/path_to_pem_file").read()
root=ElementTree.fromstring(saml_xml_token)
assertion_data=xmldsig(root)verify(x509_cert=cert)

And the last command xmldsig(root)verify(x509_cert=cert) gives me the following error: Invalid input object: Element

The full stack trace is the following:

Traceback:
File "/usr/local/lib/python2.7/dist-packages/django/core/handlers/base.py" in get_response

  1.                 response = wrapped_callback(request, _callback_args, *_callback_kwargs)
    
    1.   assertion_data=xmldsig(root).verify()
      
      File "/usr/local/lib/python2.7/dist-packages/signxml/init.py" in verify
  2.         _get_schema().assertValid(signature)
    

Exception Type: TypeError at /model_app/trySaml/
Exception Value: Invalid input object: Element

Do you have any idea what could be wrong with the code?

Thank you,
Nick

signxml vs http://www.w3.org/TR/xmldsig-core

Hello Andrey,
Can you help me with signxml?
Its lib allows signing the <Signature tag standard as http://www.w3.org/TR/xmldsig-core?
I'm trying to sign as the code below and displays error:

from signxml import signxml
from lxml import etree

cert = open("certi.pem").read()
key = open("key.pem").read()

myxml = open( 'myxml.xml' , 'r').read()
root = etree.fromstring( myxml )
xmldsig(root).sign( key=key , cert=cert , reference_uri="#ID35150537837900019955014000000009000000009")
verified_data = xmldsig(root).verify()

Traceback (most recent call last):
File "", line 1, in
File "C:\Python27\lib\site-packages\signxml__init__.py", line 591, in verify
raise InvalidDigest("Digest mismatch")
signxml.InvalidDigest: Digest mismatch

myxml =

<?xml version="1.0" encoding="utf-8" ?> 
<!DOCTYPE inutNFe (View Source for full doctype...)> 
- <inutNFe xmlns="http://www.portalfiscal.inf.br/nfe" versao="3.10">
- <infInut Id="ID35150537837900019955014000000009000000009">
<tpAmb>2</tpAmb> 
<xServ>INUTILIZAR</xServ> 
<cUF>35</cUF> 
<ano>15</ano> 
<CNPJ>05378379000199</CNPJ> 
<mod>55</mod> 
<serie>14</serie> 
<nNFIni>9</nNFIni> 
<nNFFin>9</nNFFin> 
<xJust>Testando a inutilizacao de NF-e</xJust> 
</infInut>
- <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
- <SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" /> 
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" /> 
- <Reference URI="#ID35150537837900019955014000000009000000009">
- <Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /> 
<Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" /> 
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" /> 
<DigestValue /> 
</Reference>
</SignedInfo>
<SignatureValue /> 
- <KeyInfo>
- <X509Data>
<X509Certificate /> 
</X509Data>
</KeyInfo>
</Signature>
</inutNFe>

Set Reference URI and add a transform algorithm

I've been using C# to generate a signed xml and I got something like this:
this is what i need

I can set the URI in reference, and also I have two transform algorithm (I have no idea what is used for)
The closest I got is this:
generated signatura

I tried with Signature placeholder, but couldn't find a way to change the URI.

Could you help me on this? Thanks.

Problem with signing Windows provisioning profiles

Windows Network Provisioning is not happy with the enveloped signature generated by signxml.

After digging the problem I found that second transform in the transforms node:
Working example:
<Transforms> <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /> </Transforms>

Broken example:
<ds:Transforms> <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"></ds:Transform> <ds:Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></ds:Transform></ds:Transforms>

Unfortunately at the moment there's no way I could disable second transform other than by editing the code of the signxml.

Is there a chance this could be resolved by adding some sort of switch?

Extracting public key off of v3 SSL Certificate

Hi Andrey,

I'd like to make an enhancement suggestion to the strip_pem_header procedure in signxml/util/init.py.
Currently the code is expecting a header and footer which are skipped. However v3 certificates might include additional information before the header. As a result the signature X509Certificate content is loaded with additional data that doesn't belong there, producing an invalid signature.

I'm suggesting replacing:

def strip_pem_header(cert):
    bare_base64_cert = ""
    for line in ensure_str(cert).splitlines():
        if line != PEM_HEADER and line != PEM_FOOTER:
            bare_base64_cert += line
    return bare_base64_cert

with:

def strip_pem_header(cert):
    bare_base64_cert = ""
    head = cert.index(PEM_HEADER)
    if head < 0:
        raise Exception("Certificate PEM Header Not Found")
    head += len(PEM_HEADER)
    tail = cert.index(PEM_FOOTER, head)
    if tail < head:
        raise Exception("Certificate PEM Footer Not Found")
    return cert[head:tail].strip()

Regards,
Vic.

Api cosmetics

Now the signxml object give ability to sign and verify bunch of data pass in constructor argument. Method, algorithm, cert and rest stuff are pass as argument specific method. There Is no option to sign or verify without creating an object. Why not create simple interface like:

sign(data, method, algorithm, key, passphrase, cert, c14n_algorithm, reference_uri, digest_algorithm)

or use pre-created obj to keep more accurate parameters. In most situation I sign and verify many different bunch of data with same algorithm and keys. Maybe keep those parameters in fabric object and passing data direct to sign(or veryfi) method. It more intuitive.

signer_obj = signxml.xmldsig( algorithm, key, passphrase, cert)
confirmatory_obj = signxml.xmldsig( algorithm, key_pub)
signed_data1 = signer_obj.sign(data1, method)
signed_data2 = signer_obj.sign(data2, method)
signed_data3 = signer_obj.sign(data3, method)
(send_message and get response ...)
if confirmatory_obj.veryfi(response) != get_predicted_signed_part(reponse):
    raise SomeException()

Enveloped signature with no (or empty) reference URI

Hi Andrey,

Some SaaS providers supporting SSO using SAML may reject signed assertion if enveloped signature has no (or empty) reference URI.

I'd like to suggest the following enhancement to the enveloped signature functionality which will look for and specify the reference URI based on the signed data id attribute value.

signxml/signxml/init.py:

210              raise InvalidInput("Enveloped signature input contains more than one placeholder")
211
212        self._reference_uri = ""
+++        # get signed data id attribute value for reference uri  
+++        payloadId = self.payload.get("Id", self.payload.get("ID"))
+++        if payloadId is not None:
+++            # set default reference uri based on data id attribute value
+++            self._reference_uri = "#{}".format(payloadId)
213        elif method == methods.detached:
214            if self._reference_uri is None:
215                self._reference_uri = "#{}".format(self.payload.get("Id", self.payload.get("ID", "object")))

Regards,
Vic.

Support signing data that has a pre-existing signature somewhere

The signxml._get_signature_regex() function is used to pull signature blocks out of the post-canonicalized data using a regex. When signing a payload that includes another signature, this function removes the internal signatures as well.

The end result is that the internal, original signatures remain valid but the outer, new signature created by signxml omits the inner signature blocks from the digest and is invalid

Support verification of multiple signatures

  • Use breadth-first traversal when looking for signatures
  • Silently discard signatures that don't use the expected certificate
  • If multiple signatures remain and current interface is used, raise an error
  • Introduce iterator interface for situations where multiple signatures are expected

ascii codec can't decode byte 0xC2 in position 59: ordinal not in range (128)

Dear all,

I am trying to validate an xml signature (saml token) using signxml.
I am using the following code:

cert=open("/path_to_pem_file").read()
root=ElementTree.fromstring(saml_xml_token)
assertion_data=xmldsig(root)verify(x509_cert=cert)

And the last command xmldsig(root)verify(x509_cert=cert) gives me the following error:
ascii codec can't decode byte 0xC2 in position 59: ordinal not in range (128)

I have tried using reload(sys)
sys.setdefaultencoding('utf-8')
but with no result.

Do you have any idea what could be wrong with the code?

Thank you,
Nick

Error: signxml.InvalidDigest: Digest mismatch

I'm trying to sign an xml.
I am following the example below, but gives error:

File "C: \ Python27 \ lib \ site-packages \ signxml \ __ init__.py", line 532, in verify
raise InvalidDigest ("Digest mismatch")
signxml.InvalidDigest: Digest mismatch

This is the code:

from signxml import xmldsig
from lxml import etree

cert = open("certi.pem").read()
key = open("key.pem").read()
cmyxml = open( 'myxml.xml' , 'r').read()
root = etree.fromstring( cmyxml )
xmldsig(root).sign(key=key, cert=cert)
verified_data = xmldsig(root).verify()

This is the xml on disk:

<?xml version="1.0" encoding="utf-8"?>
<NFe xmlns="http://www.tests.inf.br/tests">
   <infNFe versao="3.10" Id="NFe">
      <ide>
         <cUF>35</cUF>
         <cNF>44633102</cNF>
         <natOp>Venda</natOp>
         <indPag>1</indPag>
         <mod>55</mod>
         <serie>15</serie>
         <nNF>27</nNF>
         <dhEmi>2014-12-30T09:44:02-02:00</dhEmi>
         <tpNF>1</tpNF>
         <idDest>1</idDest>
         <cMunFG>3550308</cMunFG>
         <tpImp>1</tpImp>
         <tpEmis>1</tpEmis>
         <cDV>4</cDV>
         <tpAmb>2</tpAmb>
         <finNFe>1</finNFe>
         <indFinal>0</indFinal>
         <indPres>9</indPres>
         <procEmi>0</procEmi>
         <verProc>NF-e</verProc>
      </ide>
      <emit>
         <CNPJ>00000000000000</CNPJ>
         <xNome>Razao Social Ltda</xNome>
         <xFant>Nome Fantasia</xFant>
         <enderEmit>
            <xLgr>Rua do Emitente</xLgr>
            <nro>140</nro>
            <xBairro>Bairro</xBairro>
            <cMun>3550308</cMun>
            <xMun>TESTES</xMun>
            <UF>UF</UF>
            <CEP>00000000</CEP>
            <fone>1112345678</fone>
         </enderEmit>
         <IE>1234567890</IE>
         <CRT>3</CRT>
      </emit>
      <dest>
         <CNPJ>00000000000000</CNPJ>
         <xNome>Razao Social Ltda</xNome>
         <enderDest>
            <xLgr>Rua Teste</xLgr>
            <nro>10</nro>
            <xBairro>Bairro</xBairro>
            <cMun>3550308</cMun>
            <xMun>TESTES</xMun>
            <UF>SP</UF>
            <CEP>00000000</CEP>
            <fone>1112340000</fone>
         </enderDest>
         <indIEDest>9</indIEDest>
         <IE>000000000000</IE>
         <email>[email protected]</email>
      </dest>
      <det nItem="1">
         <prod>
            <cProd>codigo do produto</cProd>
            <cEAN />
            <xProd>Descricao do produto</xProd>
            <NCM>0000000</NCM>
            <CFOP>1234</CFOP>
            <uCom>UN</uCom>
            <qCom>1.0000</qCom>
            <vUnCom>1.0000</vUnCom>
            <vProd>1.00</vProd>
            <cEANTrib />
            <uTrib>UN</uTrib>
            <qTrib>1.0000</qTrib>
            <vUnTrib>1.0000</vUnTrib>
            <indTot>1</indTot>
         </prod>
         <imposto>
            <ICMS>
               <ICMS00>
                  <orig>0</orig>
                  <CST>00</CST>
                  <modBC>3</modBC>
                  <vBC>1.00</vBC>
                  <pICMS>18.0000</pICMS>
                  <vICMS>0.18</vICMS>
               </ICMS00>
            </ICMS>
            <PIS>
               <PISAliq>
                  <CST>01</CST>
                  <vBC>1.00</vBC>
                  <pPIS>0.65</pPIS>
                  <vPIS>0.01</vPIS>
               </PISAliq>
            </PIS>
            <COFINS>
               <COFINSAliq>
                  <CST>01</CST>
                  <vBC>1.00</vBC>
                  <pCOFINS>3.0000</pCOFINS>
                  <vCOFINS>0.03</vCOFINS>
               </COFINSAliq>
            </COFINS>
         </imposto>
         <infAdProd>obs</infAdProd>
      </det>
      <total>
         <ICMSTot>
            <vBC>1.00</vBC>
            <vICMS>0.18</vICMS>
            <vICMSDeson>0.00</vICMSDeson>
            <vBCST>0.00</vBCST>
            <vST>0.00</vST>
            <vProd>1.00</vProd>
            <vFrete>0.00</vFrete>
            <vSeg>0.00</vSeg>
            <vDesc>0.00</vDesc>
            <vII>0.00</vII>
            <vIPI>0.10</vIPI>
            <vPIS>0.01</vPIS>
            <vCOFINS>0.03</vCOFINS>
            <vOutro>0.00</vOutro>
            <vNF>1.10</vNF>
         </ICMSTot>
      </total>
      <transp>
         <modFrete>0</modFrete>
      </transp>
   </infNFe>
   <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
      <SignedInfo>
         <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
         <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
         <Reference URI="#NFe33171103459677000327540450000000277890247234">
            <Transforms>
               <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
               <Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
            </Transforms>
            <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
            <DigestValue></DigestValue>
         </Reference>
      </SignedInfo>
      <SignatureValue></SignatureValue>
      <KeyInfo>
         <X509Data>
            <X509Certificate></X509Certificate>
         </X509Data>
      </KeyInfo>
   </Signature>
</NFe>

Incompabilities with the SII schema

Signatures generated with signxml are currently not compatible with the XSD that the SII (Chile’s IRS) uses.

I am currently using a fork of signxml in order to sign SII documents, with the following changes:

  • Generate a KeyValue element even if a certificate is provided. (#52)
  • Remove the second Transform element that signxml generates, the one that describes the C14N
    algorithm.

Also, the SII expects the Signature element to be located as a sibling of the signed node, not within it. Hence, I am using a helper method for signing which receives the parent of the element to sign (which must be the only child):

def _sign(self, parent):
    children = list(parent)
    assert len(children) == 1
    element = children[0]
    element_with_signature = self._signer.sign(
        element, key=self._key, cert=self._certificate)
    signature = element_with_signature.find(
        '{http://www.w3.org/2000/09/xmldsig#}Signature')
    parent.append(signature)

I am hoping that, eventually, signxml will be usable for generating SII-compatible signatures without the need to fork it with these changes.

Problematic distribution on windows (any hints on how to switch to pure python rsa libs)?

Hi,

I made a desktop app for signing eInvoices with SignXML. I was very happy with your module, because it solved my problem quickly and it's powerfull enough that I could switch to specific canonicalizsation and algorhitms that the specific eInvoice praxis required.

But most of users use windows. While I can elegantly install modules via pip and use them various app packagers didn't work. The submodules of cryptography couldn't install properly via packaged app for example with pyinstaller. The solution was to use pynsist, which installs regular python and copies modules, and then in instalation script run pip install for signXML and required modules. The solution is not ideal for end users because it also installs separate Python and Python Launcher, but it worked.

But now it seems some cryptography modules changed and the same instalation that worked doesn't work any more (app fails with error from cryptography.hazmat.primitives import interfaces
ImportError: cannot import name interfaces). I didn't yet figure out what is the problem. I thought the version of cryptography changed on pip but it doesn't seem to, if I install it manually in regular python both new and old versions work, with installation both of then don't.

Given the imperfect instalation and now that I can't even make it work I see two ways to solve this. Either I dump this and make app again in some other language or I can make SignXML work with pure python libs (like pyrsa), hence being able to package it with pyinstaller and removing the concrete problem I have now. Also, I don't need all the algo options SignXML offers but a smaller subset.

Do you have any oppinion on this? Could rewriting SignXML to use pure python work or are there any issues you think can't be solved without OpenSSL and other navice libs?

Is anyone else sucsessfully deploying SignXML application on Windows?

Thank for making this,
Jankos

Wrong Signature Value

Hello.

I´m having a little problem with a xml digital signature. I have a working example in php and when I generate the same digital signature it gives me a different signature value. everything is the same except for the signature value. could you help me to find out what I´m missing? i attached what I´m using

signXML.zip

xmldsig vs http://www.w3.org/TR/xmldsig-core

Hello Andrey,
Can you help me with your [email protected]?
Its lib allows signing the <Signature tag standard as http://www.w3.org/TR/xmldsig-core?
I'm trying to sign as the code below and displays error:

from signxml import xmldsig
from lxml import etree

cert = open("certi.pem").read()
key = open("key.pem").read()

myxml = open( 'myxml.xml' , 'r').read()
root = etree.fromstring( myxml )
xmldsig(root).sign( key=key , cert=cert , reference_uri="#ID35150537837900019955014000000009000000009")
verified_data = xmldsig(root).verify()

Traceback (most recent call last):
File "", line 1, in
File "C:\Python27\lib\site-packages\signxml__init__.py", line 591, in verify
raise InvalidDigest("Digest mismatch")
signxml.InvalidDigest: Digest mismatch

myxml =

<?xml version="1.0" encoding="utf-8" ?> 
<!DOCTYPE inutNFe (View Source for full doctype...)> 
- <inutNFe xmlns="http://www.portalfiscal.inf.br/nfe" versao="3.10">
- <infInut Id="ID35150537837900019955014000000009000000009">
<tpAmb>2</tpAmb> 
<xServ>INUTILIZAR</xServ> 
<cUF>35</cUF> 
<ano>15</ano> 
<CNPJ>05378379000199</CNPJ> 
<mod>55</mod> 
<serie>14</serie> 
<nNFIni>9</nNFIni> 
<nNFFin>9</nNFFin> 
<xJust>Testando a inutilizacao de NF-e</xJust> 
</infInut>
- <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
- <SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" /> 
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" /> 
- <Reference URI="#ID35150537837900019955014000000009000000009">
- <Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" /> 
<Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" /> 
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" /> 
<DigestValue /> 
</Reference>
</SignedInfo>
<SignatureValue /> 
- <KeyInfo>
- <X509Data>
<X509Certificate /> 
</X509Data>
</KeyInfo>
</Signature>
</inutNFe>

Support xenc:EncryptedKey and xenc:EncryptedData

In order to support saml2:EncryptedAssertion, it would be great to be able to pass a private key to a method that decrypts the CipherData.

I am happy to do the work, given pointers to get me started.

Attached is an example section of XML generated by Okta.

encrypted.xml.txt

Detached signing: Combining multiple SignedInfo elements to attach to a SOAP header

I'm currently trying to use this library to sign an RST request using SOAP1.2/WS-SecureConversation (a subset of WS-SE and WS-Trust). Since there doesn't appear to exist any implementations of this as of today, I have to make it on my own.

What the RST looks like before signing (redacted some info, but nothing crucial):

<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
  <soap:Header>
    <Action xmlns="http://www.w3.org/2005/08/addressing">http://schemas.xmlsoap.org/ws/2005/02/trust/RST/SCT</Action>
    <MessageID xmlns="http://www.w3.org/2005/08/addressing">urn:uuid:cc1549c2-3fa8-439a-b12e-8170288a34f7</MessageID>
    <To wsu:Id="9ef68234-476e-4797-8f31-73c0cae42eab" xmlns="http://www.w3.org/2005/08/addressing" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">service_url</To>
    <ReplyTo xmlns="http://www.w3.org/2005/08/addressing">
      <Address>http://www.w3.org/2005/08/addressing/anonymous</Address>
    </ReplyTo>
    <wsse:Security soap:mustUnderstand="true" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
      <wsse:BinarySecurityToken EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" wsu:Id="X509-26ac00cb-9651-4e45-ae8f-6d60fc101bcb">#####</wsse:BinarySecurityToken>
      <wsu:Timestamp wsu:Id="TS-f9269060-f3cd-44b9-b55d-08261909ed56">
        <wsu:Created>2016-08-30T09:10:09.177863+00:00</wsu:Created>
        <wsu:Expires>2016-08-30T09:10:09.177863+00:00</wsu:Expires>
      </wsu:Timestamp>
    </wsse:Security>
  </soap:Header>
  <soap:Body>
    <wst:RequestSecurityToken xmlns:wst="http://schemas.xmlsoap.org/ws/2005/02/trust">
      <wst:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</wst:RequestType>
      <wsp:AppliesTo xmlns:wsp="http://www.w3.org/ns/ws-policy">
        <wsa:EndpointReference xmlns:wsa="http://www.w3.org/2005/08/addressing">
          <wsa:Address>service_url</wsa:Address>
        </wsa:EndpointReference>
      </wsp:AppliesTo>
      <wst:Lifetime xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
        <wst:Created>2016-08-30T09:10:09.178001+00:00</wst:Created>
        <wst:Expires>2016-08-30T09:15:09.178001+00:00</wst:Expires>
      </wst:Lifetime>
      <wst:TokenType>http://schemas.xmlsoap.org/ws/2005/02/sc/sct</wst:TokenType>
      <wst:KeySize>256</wst:KeySize>
      <wst:Entropy>
        <wst:BinarySecret Type="http://schemas.xmlsoap.org/ws/2005/02/trust/Nonce">###</wst:BinarySecret>
      </wst:Entropy>
      <wst:ComputedKeyAlgorithm>http://schemas.xmlsoap.org/ws/2005/02/trust/CK/PSHA1</wst:ComputedKeyAlgorithm>
      <wst:Renewing/>
    </wst:RequestSecurityToken>
  </soap:Body>
</soap:Envelope>

Before this request can be dispatched, it needs to be signed. Specifically, the elements <To> and <Timestamp>. By using the reference_uri argument I can pass each element separately, but the problem is that this also produces two <SignedInfo> fields, and the field <SignatureValue> will of course not be correctly computed (I also need to customize <KeyInfo> after signing, it has to contain a token reference with a SHA1 thumbprint instead of the x509-data, I'm not sure how this affects the validity of the signature?).

Anyway, any suggestions as to how I can make this work? This is the final piece of the (pretty big) puzzle to send this request, and I really like the structure of this library. I'm not expecting this to be supported as is, but perhaps there could be a workaround?

Unable to get local issuer certificate

Hey,

I'm trying to use the signxml to sign Brazilian tax documents (called NFSe). I'm signing this documents from a .pfx certificate, which I extracted the private key and public cert. I sign with the follow command:

xmldsig(xml, digest_algorithm="sha1").sign( algorithm="rsa-sha1", key=self.key, cert=self.cert, c14n_algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315")

The webservice always return signature error, and the verify method return "Unable to get local issuer certificate". I know I need the certificate chain, I have it, is a .cer file, but I don't know which parameter I use to verify with the chain, I've tried ca_path and can_pem_file, none of these worked.
Another thing, the webservice says that I need the ID in the xml root, this is weird because the transformer is enveloped. I have the xsd's if it help.

Thanks

Compatibility with PyInstaller

When creating a package with pyinstaller I've an issue because xmldsig1-schema.xsd is not included. The application is executed in a temporary directory, and the file path should be for example C:\\Users\\ADMINI~1\\AppData\\Local\\Temp\\2\\_MEI46203\\signxml\\schemas\\xmldsi g1-schema.xsd

A potential workaround would be to specify at run-time the directory path containing xmldsig1-schema.xsd and the other schemas. es:

signxml.xmldsig(rootXML, schemas_dir=...)

Is it feasible?

AttributeError: 'Error' object has no attribute 'message'

I geht this message when calling
verified_et_element = signxml.xmldsig(xml_bytes).verify(x509_cert=cert)

Traceback
  File "/Users/admin/virtualenvs/pvzd34/lib/python3.4/site-packages/signxml/__init__.py", line 631, in verify
    lib, func, reason = e.message[0]

Sign from a .pfx certificate

Hi, sorry for the newbie request.
I know that is possible to convert a pfx file to the pem format with PyOpenSSL, but what about override the sign with the possibility to receive a pfx file?

Nice library, a good alternative to pyxmlsec 👍

validation against custom CA

I need to validate signatures against a project-specific CA. I tried:

cert file: signxml.xmldsig(xml_bytes).verify(ca_pem_file=mycacert.pem)
This raises OpenSSL.crypto.X509StoreContextError: [18, 0, 'self signed certificate']

Traceback: 
  [..]/signxml/__init__.py", line 694, in verify_x509_cert_chain
    X509StoreContext(store, cert).verify_certificate()
  [..]/crypto.py, line 1571, in verify_certificate
    raise self._exception_from_context()

cert dir: signxml.xmldsig(xml_bytes).verify(ca_path=mycadir)
this raises AttributeError: 'NoneType' object has no attribute 'encode'. I think that it is a bug, because ensure_bytes() does not allow None arguments.

 Traceback: 
   [..]/signxml/__init__.py", line 690, in verify_x509_cert_chain
     context.load_verify_locations(ensure_bytes(ca_pem_file), capath=ca_path)
   [..]/signxml/util/__init__.py", line 20, in ensure_bytes
     x = x.encode(encoding)

Aslo, what would be a reliable method to obtain the signing certificate? I need to check certs against a list of allowed signers.

xmldsig throwing signxml.InvalidInput

I attempting to verify a SAML assertion with the following code:

from lxml import etree
import base64
from signxml import xmldsig
import xml.etree.ElementTree as ET

decoded_assertion = base64.b64decode(assertion)
root = etree.XML(decoded_assertion)
signature_node = root.find('{http://www.w3.org/2000/09/xmldsig#}Signature')
signature_value = signature_node.find('{http://www.w3.org/2000/09/xmldsig#}SignatureValue').text
signed_info = signature_node.find('{http://www.w3.org/2000/09/xmldsig#}SignedInfo')
signed_info_string_c14n = etree.tostring(signed_info,method="c14n")

certificate_node = root.find('{http://www.w3.org/2000/09/xmldsig#}Signature')\
        .find('{http://www.w3.org/2000/09/xmldsig#}KeyInfo')\
        .find('{http://www.w3.org/2000/09/xmldsig#}X509Data')\
        .find('{http://www.w3.org/2000/09/xmldsig#}X509Certificate')

result = xmldsig(signature_node).verify(x509_cert=certificate_node.text)

I am getting an exception:

Expected to find XML element Object[@id="SAML-..."] in {http://www.w3.org/2000/09/xmldsig#}Signature (<class 'signxml.InvalidInput'>)

I've tried passing in a few different versions of signature_node to no avail.

ca_path isn't finding proper certificate

If I set ca_path in verify, it fails:

>>> ds.verify(id_attribute='xml:id', ca_path="/home/nbastin/geni-roots", validate_schema=False)                                                                                     
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/usr/local/lib/python2.7/dist-packages/signxml/__init__.py", line 657, in verify
    verify_x509_cert_chain(cert_chain, ca_pem_file=ca_pem_file, ca_path=ca_path)
  File "/usr/local/lib/python2.7/dist-packages/signxml/__init__.py", line 735, in verify_x509_cert_chain
    raise InvalidCertificate(e)
InvalidCertificate: [20, 0, 'unable to get local issuer certificate']

But if I pass it the proper PEM in ca_pem_file, which is in that path, it "works" (sortof - not sure what this other problem is):

>>> ds.verify(id_attribute='xml:id', ca_pem_file="geni-roots/ch.geni.net-ca.pem", validate_schema=False)                                                                            
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/usr/local/lib/python2.7/dist-packages/signxml/__init__.py", line 671, in verify
    raise InvalidSignature("Signature verification failed: {}".format(reason))
InvalidSignature: Signature verification failed: bad signature

Syntax error during build of asn1.py

During a pip install on Ubuntu 14.04 shows this error during the build:

build/signxml/signxml/util/asn1.py'...
File "..build/signxml/signxml/util/asn1.py", line 295
self.value = 0L
^
SyntaxError: invalid syntax

The build completes, but this is scary looking.

Add documentation for signing with cert chain

e.g. letsencrypt

NB: find out what the standard chain presentation order is: in xmldsig interop materials it appears to be (intermediates, cert), in fullchain.pem files handled by openssl it's (cert, intermediates)

Clarify example of SAML validation

Per #41, make sure the following use case is covered in the docs:

import base64, lxml.etree, signxml
md_dom = lxml.etree.parse('metadata.xml')
md_root = md_dom.getroot()
with open('metadata_crt.pem', 'r') as fd:
    md_cert_pem = fd.read()
asserted_metadata = signxml.xmldsig(md_root).verify(x509_cert=md_cert_pem)

Update cryptography dependency to cryptography>=1.1.2

As I see, signxml depends on cryptography<1.1,>=1.0.2 but cryptography<1.1.2 has a runtime error undefined symbol EC_GFp_nistp224_method with some OpenSSL installations.

I'm trying to run signxml (pip install signxml) on Fedora 23 and I'm getting undefined symbol EC_GFp_nistp224_method runtime error from cryptography library. I updated cryptography to v1.2.3 (pip install --upgrade cryptography) and everything is working fine so far.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.