xmlsec / python-xmlsec

Python bindings for the XML Security Library.
MIT License
95 stars 99 forks source link

Break line in tag signature #196

Open edsonbernar opened 3 years ago

edsonbernar commented 3 years ago

Hi, Is it possible to avoid line breaks in the signature tag that is generated? Some servers refuse these characters.

<NFe xmlns="http://www.portalfiscal.inf.br/nfe"><infNFe versao="4.00"><ide><cUF>35</cUF><cNF>1212</cNF><natOp>VENDA MERC. ADQ. OU REC. DE TERC</natOp><mod>55</mod><serie>1</serie><nNF>1242</nNF></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="#NFe35211107457285000133550010010010351942498581">
<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>RgXK0snmSsBVA4KbqOnH1jhPZOI=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>ovwmdOLfXapXCvXF39</SignatureValue>
<KeyInfo>
<X509Data>
<X509Certificate>MIIIHjCCBgagAwIBAgI</X509Certificate>
</X509Data>
</KeyInfo>
</Signature></NFe>

Code

element_signed = xml_element.find(".//*[@Id='%s']" % reference)
parent = element_signed.getparent()        
ref_uri = "#NFe35211107457285000133550010010010351942498581"

signature_node = xmlsec.template.create(
            element_signed,
            c14n_method=consts.TransformInclC14N,
            sign_method=consts.TransformRsaSha1,
        )      
parent.append(signature_node)

ref = xmlsec.template.add_reference(signature_node, xmlsec.Transform.SHA1, uri=ref_uri)

xmlsec.template.add_transform(ref, consts.TransformEnveloped)
xmlsec.template.add_transform(ref, consts.TransformInclC14N)               

ki = xmlsec.template.ensure_key_info(signature_node)
xmlsec.template.add_x509_data(ki)

ctx = xmlsec.SignatureContext()
ctx.key = key
ctx.key.load_cert_from_memory(self.certificado, consts.KeyDataFormatPem)        
ctx.register_id(element_signed, id_attr='Id')

ctx.sign(signature_node)

return etree.tostring(xml_element, encoding=str)
hoefling commented 2 years ago

@edsonbernar what line breaks do you mean exactly? The string representation of the XML tree is generated by lxml's etree.tostring() function, this isn't part of xmlsec.

edsonbernar commented 2 years ago

Hi, The code 'return etree.tostring(xml_element, encoding=str)' is just my output. Signing takes place before that.

If you add the code below before 'ctx.sign(signature_node)' then the signature is generated in the correct pattern.

signature_node = etree.tostring(
signature_node, encoding=str).replace('\n','')
signature_node = etree.fromstring(signature_node)
parent.append(signature_node)
ctx.sign(signature_node)

It seems to me that 'xmlsec.template.create()' introduces line breaks throughout the signature node. At least that's what I notice when I open the signed xml in an editor. Using another library to sign (signxml), the signature node does not come out with line breaks.

hoefling commented 2 years ago

If you mean the line breaks in the base64-encoded signature value, then you can control the maximal line size via xmlsec.base64_default_line_size, e.g.

import xmlsec
xmlsec.base64_default_line_size(size=4096)
...

Otherwise, please provide a reproducible example that illustrates what you mean exactly.

edsonbernar commented 2 years ago

I already use 'xmlsec.base64_default_line_size' to avoid line breaks in the certificate. I will try to make the xml signature with the 'Signature' element already existing in the xml, without using 'xmlsec.template.create' to create and then add.

edsonbernar commented 2 years ago

I tested it and the result was the same. Because of the breaks the 'SignatureValue' is different from other libraries (.Net, Python Signxml) The 'DigestValue' is calculated the same in all. Anyway thanks for the help.