Skip to content

Instantly share code, notes, and snippets.

@marcelcaraciolo
Created February 1, 2014 18:52
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save marcelcaraciolo/8756826 to your computer and use it in GitHub Desktop.
Save marcelcaraciolo/8756826 to your computer and use it in GitHub Desktop.
import os, sys
# sys.path.insert(0, '../')
import libxml2
import xmlsec
def start():
# Init libxml library
libxml2.initParser()
libxml2.substituteEntitiesDefault(1)
# Init xmlsec library
if xmlsec.init() < 0:
print "Error: xmlsec initialization failed."
return sys.exit(-1)
# Check loaded library version
if xmlsec.checkVersion() != 1:
print "Error: loaded xmlsec library version is not compatible.\n"
sys.exit(-1)
# Init crypto library
if xmlsec.cryptoAppInit(None) < 0:
print "Error: crypto initialization failed."
# Init xmlsec-crypto library
if xmlsec.cryptoInit() < 0:
print "Error: xmlsec-crypto initialization failed."
def end():
# Shutdown xmlsec-crypto library
xmlsec.cryptoShutdown()
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# WARNING: I'm initializing again after the shutdown. This is because of a bug in the
# cryptoAppShutdown() function, which for some reason deactivates the OpenSSL engine.
# The OpenSSL is required further by the HTTPS connection to the NFSE's WebService.
# Therefore, I had to find out a way to not disable the OpenSSL proper functioning.
# 1. I could just not shutdown the crypto App. By doing this, the HTTPS works normally,
# but I could not use XMLSec again a second time.
# 2. I could shutdown and initialize again (chosen solution). By doing this, I could
# keep working normally the HTTPS as well another use of XMLSec.
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# Shutdown crypto library
xmlsec.cryptoAppShutdown()
xmlsec.cryptoAppInit(None)
# Shutdown xmlsec library
xmlsec.shutdown()
# Shutdown LibXML2
libxml2.cleanupParser()
# Signs the xml_file using private key from key_file and dynamicaly
# created enveloped signature template. The certificate from cert_file
# is placed in the <dsig:X509Data/> node.
# Returns 0 on success or a negative value if an error occurs.
def sign_file(xml_file, key_file, cert_file, node_name, output_file):
start()
# Load template
if not check_filename(xml_file):
return -1
doc = libxml2.parseFile(xml_file)
if doc is None or doc.getRootElement() is None:
print "Error: unable to parse file \"%s\"" % xml_file
return cleanup(doc)
# Add <dsig:Signature/> node to the doc
ctxt = doc.xpathNewContext()
result = ctxt.xpathEval("//*")
result_nodes = []
for node in result:
if node.name == node_name:
result_nodes.append(node)
if not result_nodes:
print 'Error: failed to find the node %s' % node_name
return cleanup(doc)
for result_node in result_nodes:
# Create signature template for RSA-SHA1 enveloped signature
signNode = xmlsec.TmplSignature(doc, xmlsec.transformExclC14NId(),
xmlsec.transformRsaSha1Id(), None)
if signNode is None:
print "Error: failed to create signature template"
return cleanup(doc)
#doc.getRootElement().addChild(signNode)
result_node.addChild(signNode)
# Add reference
refNode = signNode.addReference(xmlsec.transformSha1Id(),
None, None, None)
if refNode is None:
print "Error: failed to add reference to signature template"
return cleanup(doc)
# Add enveloped transform
if refNode.addTransform(xmlsec.transformEnvelopedId()) is None:
print "Error: failed to add enveloped transform to reference"
return cleanup(doc)
# Add <dsig:KeyInfo/> and <dsig:X509Data/>
keyInfoNode = signNode.ensureKeyInfo(None)
if keyInfoNode is None:
print "Error: failed to add key info"
return cleanup(doc)
if keyInfoNode.addX509Data() is None:
print "Error: failed to add X509Data node"
return cleanup(doc)
# Create signature context, we don't need keys manager in this example
dsig_ctx = xmlsec.DSigCtx()
if dsig_ctx is None:
print "Error: failed to create signature context"
return cleanup(doc)
# Load private key, assuming that there is not password
if not check_filename(key_file):
return cleanup(doc, dsig_ctx)
key = xmlsec.cryptoAppKeyLoad(key_file, xmlsec.KeyDataFormatPem,
pwd=None, pwdCallback=None, pwdCallbackCtx=None)
if key is None:
print "Error: failed to load private pem key from \"%s\"" % key_file
return cleanup(doc, dsig_ctx)
dsig_ctx.signKey = key
# Load certificate and add to the key
if not check_filename(cert_file):
return cleanup(doc, dsig_ctx)
if xmlsec.cryptoAppKeyCertLoad(key, cert_file, xmlsec.KeyDataFormatPem) < 0:
print "Error: failed to load pem certificate \"%s\"" % cert_file
return cleanup(doc, dsig_ctx)
# Set key name to the file name, this is just an example!
if key.setName(key_file) < 0:
print "Error: failed to set key name for key from \"%s\"" % key_file
return cleanup(doc, dsig_ctx)
# Sign the template
if dsig_ctx.sign(signNode) < 0:
print "Error: signature failed"
return cleanup(doc, dsig_ctx)
# Print signed document to stdout
#doc.dump("-")
doc.saveTo(output_file)
# Success
return cleanup(doc, dsig_ctx, 1)
def cleanup(doc=None, dsig_ctx=None, res=-1):
if dsig_ctx is not None:
dsig_ctx.destroy()
if doc is not None:
doc.freeDoc()
end()
return res
def check_filename(filename):
if os.access(filename, os.R_OK):
return 1
else:
print "Error: XML file \"%s\" not found OR no read access" % filename
return 0
# if __name__ == '__main__':
# sign_file('hello.xml', 'dsaprivkey.pem', 'dsacert.pem')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment