Created
February 1, 2014 18:52
-
-
Save marcelcaraciolo/8756826 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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