GithubHelp home page GithubHelp logo

xml-security / signxml Goto Github PK

View Code? Open in Web Editor NEW
139.0 11.0 105.0 3.01 MB

Python XML Signature and XAdES library

Home Page: https://xml-security.github.io/signxml/

License: Apache License 2.0

Python 96.41% XSLT 0.49% Makefile 1.69% JavaScript 1.40%
xml-signature xml-security saml saml2 ws-security xmldsig xmlsec python xades

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

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

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

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.

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

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.

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

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!

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

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.

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.

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`

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?

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.

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.

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>

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

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)

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.

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?

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.

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

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.

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()

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 👍

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

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

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]

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

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.

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?

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)

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>

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

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

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.

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?

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?

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.