Reputation: 421
I am new to xml signatures and currently I am using xmlsec to generate a signed xml. I do this with some modifications on the sample code:
from lxml import etree
import xmlsec
parser = etree.XMLParser(remove_blank_text=True)
template = etree.parse('unsigned.xml', parser).getroot()
signature_node = xmlsec.tree.find_node(template, xmlsec.constants.NodeSignature)
ctx = xmlsec.SignatureContext()
key = xmlsec.Key.from_file('keys/private_key.pem', xmlsec.constants.KeyDataFormatPem)
ctx.key = key
sig_ = ctx.sign(signature_node)
formated = etree.tostring(template)
with open('signed_test.xml', 'wb') as the_file:
the_file.write(formated)
Now I have signed XML, and from here I am trying to learn where or how the values get generated. I am following this for context. I am able to verify the DigestValue
and now I am trying to get the SignatureValue
. From the link and some other questions here in stackoverflow, I just need to:
SignedInfo
element In order to get the signature value. I am canonizing the SignedInfo
Element using lxml
:
from lxml import etree
parser = etree.XMLParser(remove_blank_text=True)
xmlTree = etree.parse('signed_info.xml', parser)
root = xmlTree.getroot()
formated = etree.tostring(root, method='c14n', exclusive=True)
# Write to file
with open('canon_sinfo.xml', 'wb') as the_file:
the_file.write(formated)
For info the following is the resulting SignedInfo
:
<SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></CanonicalizationMethod><SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"></SignatureMethod><Reference URI="#obj"><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#base64"></Transform></Transforms><DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"></DigestMethod><DigestValue>izbIdQ4tSAg6VKGpr1zd6kU9QpVQi/Bcwxjxu/k2oKk=</DigestValue></Reference></SignedInfo>
I am using cryptography to try and generate the SignatureValue
however, I cannot get the same result as that of what xmlsec generated.
Here is my code snippet in doing so:
sign_info = '''String of the Sign Info'''
digest_sinfo = hashes.Hash(hashes.SHA256(), backend=default_backend())
digest_sinfo.update(bytes(sign_info, 'utf-8'))
digested_sinfo = digest_sinfo.finalize()
# Load private_key here...
# Sign the message
signature_1v15 = private_key.sign(
digested_sinfo,
padding.PKCS1v15(),
hashes.SHA256()
)
I also tried loading the SignedInfo
into a seperate file, however I still get a mismatched SignatureValue. How do I accomplish this?
Upvotes: 1
Views: 1092
Reputation: 26
Your issue is that you do the hashing twice. The sign()
function does the hashing for you so you can skip the middle part.
Just call the sign()
with your canonized SignedInfo element:
signature_1v15 = private_key.sign(
sign_info,
padding.PKCS1v15(),
hashes.SHA256()
)
Upvotes: 1