{
"info": {
"author": "Dieter Maurer",
"author_email": "dieter@handshake.de",
"bugtrack_url": null,
"classifiers": [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: BSD License",
"Operating System :: OS Independent",
"Operating System :: POSIX :: Linux",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Topic :: Utilities"
],
"description": "This package contains a Cython (http://cython.org/) based bindung\nto Aleksey Sanin's XML security library (\"http://www.aleksey.com/xmlsec\")\nto be used together with lxml (http://lxml.de), the most popular Python\nbinding to the Gnome XML library libxml2 (http://xmlsoft.org).\n\n\nInstallation\n============\n\nInstallation of this package requires that you have previously installed\n`setuptools` (http://pypi.python.org/pypi/setuptools) or an equivalent\npackage manager.\n\nIn addition, you must have installed the development packages for\nlibxml2 and the XML security library (often called ``libxmlsec1``)\non the operating system level.\n\nThe installation will install ``lxml``, if not yet already installed.\n\nThis package interfaces with ``lxml`` via its Cython interface\n(described in ``etreepublic.pxd``). Some operating system installations\nfor ``lxml`` lack the respective files. In those cases, you may need to\ndownload an `lxml` source distribution and let the environment\nvariable ``LXML_HOME`` point to its root.\n\n``xmlsec`` can use different cryptographic engines (currently ``openssl``,\n``gnutls`` and ``nss``). By default, this package configures\n``xmlsec`` to use its default engine. Should you require a different\nengine, you can set the envvar ``XMLSEC_CRYPTO_ENGINE`` to the corresponding\nvalue. In this case, you may need to pass the name of your crypto engine\nto the ``initialize`` function.\n\nI have tried installation only on Linux, it may not work on other\nplatforms.\n\n\nImportant differences with ``xmlsec``\n=====================================\n\nObject orientation\n------------------\n\n``xmlsec`` is a \"C\" library. As such, it provides its main functionality\nby functions. Most of the functions are primarily associated with \na special kind of data structure (key, keys manager, signature/encryption\ncontext, template) where the association is expressed in the\nfunction name and the type of its first argument.\n\nThis binding uses classes to represent the concepts ``Key``,\n``KeysMngr`` (keys manager), ``DSigCtx`` (digital signature context)\nand ``EncCtx`` (encryption context) and the module ``tmpl`` to\nprovide access to templates.\n\nThe names (for functions, methods, constants) are usually derived\nfrom the respective ``xmlsec`` names but prefixes determined\nby the binding environment are removed and for functions/methods\nthe first letter is decapitalized. For example, the xmlsec\n``xmlSecDSigCtxSign`` function is represented by the ``sign`` method\nof the ``DSigCtx`` class. There are some exceptions to the\nrule. For example, the xmlsec ``xmlSecCryptoAppDefaultKeysMngrAdoptKey``\nfunction becomes the ``addKey`` method of class ``KeysMngr``\nreflecting that we only support \"default keys managers\" and that\nwe do not support keys adoption (but copy the key).\n\n\nKeys\n----\n\n``xmlsec`` treats keys as somewhat \"volatile\" objects: they are normally created\nbut are then passed over to either a ``KeysMngr`` or a signature/encryption\ncontext which then control the keys lifetime (and validity). This semantics is\na bit difficult to emulate in Python - and I decided not to try.\nInstead, I model keys as normal Python objects (with independent\nlifetime) **but** copy the encapsulated ``xmlsec`` key whenever\nit is passed over to a ``KeysMngr`` or a signature/encryption\ncontext. This has important ramifications: you do not need to worry\nabout the validity of a key (it is valid as long as you have a handle to it);\nhowever, modifications to a key have no effects on the (copied)\n``xmlsec`` key previously passed over to a keys manager or\nsignature/encryption context. This forces some changes in the\nway keys are handled in the standard ``xmlsec`` examples (below).\n\nAs a consequence, signature/encryption contexts allow the setting\nof a key but you cannot retrieve them again.\n\nShould experience show that this is too confusing or restricting,\nI may change the modeling of keys in future versions.\n\n\nAttribute access\n----------------\n\n``xmlsec`` is not completely homogenous with respect to attribute access.\nSometimes, the attribute is accessed directly; other times, there\nare \"get/set\" methods. This binding never uses \"get/set\" methods but\nalways properties.\n\n\nId suffix suppression\n---------------------\n\n``xmlsec`` identifies many objects (transforms, algoritms, key types) by ids\nand emphasizes this by appending `Id` to the corresponding names.\nIn my view, this is an irrelevant implementation detail and I have\nsuppressed all `Id` suffixes.\n\n\nAccessing encryption/decryption results\n---------------------------------------\n\n``xmlsec`` encryption/decryption can either operate on binary data\nor nodes in an XML tree. In the latter case, the tree is modified in place.\n\nWith ``lxml``, you do not have direct access to the tree; instead, you\naccess it via proxy objects referencing nodes in the tree.\nThere is a good chance that some of those proxy objects get \"confused\"\nwhen the tree is changed: they can behave surprisingly after the change.\n\nAs a consequence, you should avoid accessing a tree after\nan encryption/decryption operation via ``lxml`` references you\nhave set up before the operation. Especially, the reference\nto the encrypted/decrypted node (usually) **does not** reflect the result\nand you should consider its value as undefined.\nIf the operation has\noperated on the root of the tree, the same applies to the\n``lxml`` element tree for the tree.\n\nIn order for you to access the operation result in a safe way, the\nencryption/decryption methods return it. For ``encryptXml``, the result\nis the (``lxml`` reference to the) ``EncryptedData`` node representing\nthe encrypted part of the tree. If ``decrypt`` results in an XML result\n(rather than binary data), then its result specifies the root of\nthe decrypted subtree (which usually is not the root of the whole tree).\nApplying ``getroottree`` to an XML result of ``encryptXml``/``decrypt``\ngives a (new) safe reference to the whole tree.\n\nThe ``xmlsec`` examples have been modified to reflect this peculiarity\nof the ``lxml`` binding.\n\n\nFailure handling\n----------------\n\nAs a C library, ``xmlsec`` mostly uses return codes to indicate success/failure\nfor its functions.\nIn rare situations, a \"status\" field needs to be checked as well. \nIn Python, we have exceptions. Thus, I change failure handling: any\nfailure is indicated by an exception. Currently, there are two exception\nclasses: the base class ``Error`` and a derived class ``VerificationError``.\n``Error`` is used whenever the return code of an ``xmlsec`` function\nindicates a failure. ``VerificationError`` is used for signature\nverification when the ``verify`` call returned an \"ok\" status but\nthe status field in the context indicates that the verification failed.\n\n\n\nBinding extent\n--------------\n\nThe binding is by far not complete. It only covers ``Key``,\n``KeysMngr`` and signature/encryption context as far as required\neither by me or the examples.\n\nIt is likely that the binding will become more complete over time.\n\n\nDocumentation\n=============\n\nI do not like separate documentation (apart from overviews).\nI am a fan of documentation derived automatically from the source -- if\npossible available directly inside the Python session.\nAs a consequence, you can use ``pydoc`` or Python's ``help`` builtin\nto get detailed documentation (apart from looking at the source and\nreading this overview).\n\n\nExamples\n========\n\nThis section shows how the XML security library examples from\nhttp://www.aleksey.com/xmlsec/api/xmlsec-examples.html\nlook in Python.\n\nFor background, please also read\nhttp://www.aleksey.com/xmlsec/api/xmlsec-notes-sign-encrypt.html\nand\nhttp://www.aleksey.com/xmlsec/api/xmlsec-notes-verify-decrypt.html\n\n\nInitialization\n--------------\n\nAlways ensure that the ``xmlsec`` library is properly initialized.\nOtherwise, it fails in dubious ways. All following examples\nassume that the code below has been executed.\n\n>>> import dm.xmlsec.binding as xmlsec\n>>> xmlsec.initialize()\n\nSome imports used in our examples\n\n>>> from os.path import dirname, basename\n>>> from lxml.etree import tostring\n>>> from sys import version_info\n\n\nWe also set up some constants for the examples below.\n\n>>> BASEDIR = dirname(xmlsec.__file__) + \"/resources/\"\n\n\nThe following are helpers to hide differences between\nPython 2 and Python 3.\n\n>>> def to_text(b):\n... return b.decode(\"utf-8\") if version_info.major > 2 else b\n...\n>>> try: from io import BytesIO\n... except ImportError: from StringIO import StringIO as BytesIO\n...\n\n\nSigning an XML file\n-------------------\n\nWhat is signed actually is a standard XML file containing somewhere\na signature template. The template describes how the signature should\nbe performed and contains placeholders for the signature parts.\nThe XML security libraries examples view the complete XML file as\na template. Below is a function which signs such a template.\n\n>>> def sign_file(tmpl_file, key_file):\n... \"\"\"sign *tmpl_file* with key in *key_file*.\n... \n... *tmpl_file* actually contains an XML document containing a signature\n... template. It can be a file, a filename string or an HTTP/FTP url.\n... \n... *key_file* contains the PEM encoded private key. It must be a filename string.\n... \"\"\"\n... from lxml.etree import parse, tostring\n... doc = parse(tmpl_file)\n... # find signature node\n... node = xmlsec.findNode(doc, xmlsec.dsig(\"Signature\"))\n... dsigCtx = xmlsec.DSigCtx()\n... # Note: we do not provide read access to `dsigCtx.signKey`.\n... # Therefore, unlike the `xmlsec` example, we must set the key name\n... # before we assign it to `dsigCtx`\n... signKey = xmlsec.Key.load(key_file, xmlsec.KeyDataFormatPem, None)\n... signKey.name = basename(key_file)\n... # Note: the assignment below effectively copies the key\n... dsigCtx.signKey = signKey\n... dsigCtx.sign(node)\n... return tostring(doc)\n... \n>>> signed_file = sign_file(BASEDIR + \"sign1-tmpl.xml\", BASEDIR + \"rsakey.pem\")\n>>> print(to_text(signed_file))\n\n \n\tHello, World!\n \n \n \n \n \n \n \n \n \n \n 9H/rQr2Axe9hYTV2n/tCp+3UIQQ=\n \n \n B5tc2Kz3vc4qcTx810771Nk90qd/5p//SIAd9Ye9SIiU5vKelnvgHSy76rjTvpzE\nPszGyWA3H3JOrh/fOHmfoxdCRweuO9eDMhQADem++m55+5HTnT2K5i3IfsAID2Si\nEVOi6pGa7tmH1hXIce2uP7zSBjnKUt3nvjbFv8rK9wh7WyXXNASTa5vS8wbcaLKF\nFQGVqDVSIzyIYZVnlWPVgeIvpun6nynl4r2Az9KZxlc1Z9JXg1hJV9n6M7leL4pf\nO51M3whkD3PnFYgTgScb7qdTSTU7EzgWRmgeq3WXNTxFfXN7xozKSPGRDUj7Q5Xr\noOvoa8PZFwUwJP5A+7RCdw==\n \n\trsakey.pem\n \n \n\n\n\nSigning a dynamically created template\n--------------------------------------\n\nThis package does not bind the XML Security library template\nfunctions but implements corresponding functionality directly\nvia ``lxml``. It is implemented in module ``dm.xmlsec.binding.tmpl``\nwhich sets up a specialized parser, registers enhanced element classes for\nthe elements occuring in templates and redefines standard `lxml`\ninfrastructure (``parse``, ``Element``, ``SubElement``, ``fromstring``) to\nuse this parser. Thus, using the infrastructure provided by module ``tmpl``,\nyou can create elements or element trees in any way supported\nby ``lxml`` and when a [sub]element corresponds to an element in\na template it has additional methods to help in the template\nconstruction.\n\nIn addition, the module provides factories (``Signature`` and ``EncData``)\nwhich facilitate the creation of the top level structure of a signature\nor encryption template.\n\n>>> def sign_file_create_template(xml_file, key_file):\n... \"\"\"add signature node to *xml_file* and sign with *key_file*.\n... \n... *xml_file* can be a file, a filename string or an HTTP/FTP url.\n... \n... *key_file* contains the PEM encoded private key. It must be a filename string.\n... \"\"\"\n... # template aware infrastructure\n... from dm.xmlsec.binding.tmpl import parse, Element, SubElement, \\\n... fromstring, XML\n... from dm.xmlsec.binding.tmpl import Signature\n... \n... doc = parse(xml_file)\n... signature = Signature(xmlsec.TransformExclC14N,\n... xmlsec.TransformRsaSha1\n... )\n... doc.getroot().insert(0, signature)\n... ref = signature.addReference(xmlsec.TransformSha1)\n... ref.addTransform(xmlsec.TransformEnveloped)\n... key_info = signature.ensureKeyInfo()\n... key_info.addKeyName()\n... # now what we already know\n... dsigCtx = xmlsec.DSigCtx()\n... # Note: we do not provide read access to `dsigCtx.signKey`.\n... # Therefore, unlike the `xmlsec` example, we must set the key name\n... # before we assign it to `dsigCtx`\n... signKey = xmlsec.Key.load(key_file, xmlsec.KeyDataFormatPem, None)\n... signKey.name = basename(key_file)\n... # Note: the assignment below effectively copies the key\n... dsigCtx.signKey = signKey\n... dsigCtx.sign(signature)\n... return tostring(doc)\n... \n>>> print(to_text(sign_file_create_template(BASEDIR + \"sign2-doc.xml\", BASEDIR + \"rsakey.pem\")))\n\n HjY8ilZAIEM2tBbPn5mYO1ieIX4=GPl4vqQfQ0+b0a4mpwYXD63WA0XZEbjYvPUrCC5ySocjbnS7eofnLxpgW7AdTnaX\n3ws3zj9i184Txm26/pLu/AMQ6ezeMidod6pm5anDlRQq0WCBzxyDJo0SGo7StuFS\nkN6vRPLWr6fsnzlWdvYXCf7AXK17ANSskSNzoiQCPFYi2yISCAZlOhle9GSgMe4z\niUjrvdRU9b5zan+yBfloWw3tsRBDqcIm0xDWcUHavcn9wxuX+7QTl+B+Qe6OZJJO\n4dM1ESmjhamEFtqSiij20HSUp32AUXiKIeKnFdT4hYuacwEdF5ZXVUQ79pLBxfIR\nwlyXAHbqFba/h/Qxe8FMIQ==rsakey.pem\n\tHello, World!\n \n\n\n\nSigning with an X509 certificate\n--------------------------------\n\n>>> def sign_file_with_certificate(xml_file, key_file, cert_file):\n... \"\"\"sign *xml_file* with *key_file* and include content of *cert_file*.\n... *xml_file* can be a file, a filename string or an HTTP/FTP url.\n... \n... *key_file* contains the PEM encoded private key. It must be a filename string.\n... \n... *cert_file* contains a PEM encoded certificate (corresponding to *key_file*),\n... included as `X509Data` in the dynamically created `Signature` template.\n... \"\"\"\n... # template aware infrastructure\n... from dm.xmlsec.binding.tmpl import parse, Element, SubElement, \\\n... fromstring, XML\n... from dm.xmlsec.binding.tmpl import Signature\n... \n... doc = parse(xml_file)\n... signature = Signature(xmlsec.TransformExclC14N,\n... xmlsec.TransformRsaSha1\n... )\n... doc.getroot().insert(0, signature)\n... ref = signature.addReference(xmlsec.TransformSha1)\n... ref.addTransform(xmlsec.TransformEnveloped)\n... key_info = signature.ensureKeyInfo()\n... key_info.addKeyName()\n... key_info.addX509Data()\n... # now what we already know\n... dsigCtx = xmlsec.DSigCtx()\n... # Note: we do not provide read access to `dsigCtx.signKey`.\n... # Therefore, unlike the `xmlsec` example, we must set the certificate\n... signKey = xmlsec.Key.load(key_file, xmlsec.KeyDataFormatPem, None)\n... signKey.loadCert(cert_file, xmlsec.KeyDataFormatPem)\n... # Note: the assignment below effectively copies the key\n... dsigCtx.signKey = signKey\n... dsigCtx.sign(signature)\n... return tostring(doc)\n... \n>>> print(to_text(sign_file_with_certificate(BASEDIR + \"sign3-doc.xml\", BASEDIR + \"rsakey.pem\", BASEDIR + \"rsacert.pem\")))\n\n HjY8ilZAIEM2tBbPn5mYO1ieIX4=GPl4vqQfQ0+b0a4mpwYXD63WA0XZEbjYvPUrCC5ySocjbnS7eofnLxpgW7AdTnaX\n3ws3zj9i184Txm26/pLu/AMQ6ezeMidod6pm5anDlRQq0WCBzxyDJo0SGo7StuFS\nkN6vRPLWr6fsnzlWdvYXCf7AXK17ANSskSNzoiQCPFYi2yISCAZlOhle9GSgMe4z\niUjrvdRU9b5zan+yBfloWw3tsRBDqcIm0xDWcUHavcn9wxuX+7QTl+B+Qe6OZJJO\n4dM1ESmjhamEFtqSiij20HSUp32AUXiKIeKnFdT4hYuacwEdF5ZXVUQ79pLBxfIR\nwlyXAHbqFba/h/Qxe8FMIQ==\nMIID3zCCAscCCQCsJYoNNCLPzjANBgkqhkiG9w0BAQUFADCBszELMAkGA1UEBhMC\nREUxETAPBgNVBAgTCFNhYXJsYW5kMRIwEAYDVQQHEwlFcHBlbGJvcm4xGjAYBgNV\nBAoTEWRtLnhtbHNlYy5iaW5kaW5nMSEwHwYDVQQLExhFeGFtcGxlIFJvb3QgQ2Vy\ndGlmaWNhdGUxGjAYBgNVBAMTEWRtLnhtbHNlYy5iaW5kaW5nMSIwIAYJKoZIhvcN\nAQkBFhNkaWV0ZXJAaGFuZHNoYWtlLmRlMB4XDTEyMDYxNTE0Mzg1NFoXDTMxMDgx\nNTE0Mzg1NFowga4xCzAJBgNVBAYTAkRFMREwDwYDVQQIEwhTYWFybGFuZDESMBAG\nA1UEBxMJRXBwZWxib3JuMRowGAYDVQQKExFkbS54bWxzZWMuYmluZGluZzEcMBoG\nA1UECxMTRXhhbXBsZSBjZXJ0aWZpY2F0ZTEaMBgGA1UEAxMRZG0ueG1sc2VjLmJp\nbmRpbmcxIjAgBgkqhkiG9w0BCQEWE2RpZXRlckBoYW5kc2hha2UuZGUwggEiMA0G\nCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDG2XhPbbMvKvwFRZ68Rk/gAGfz80Jw\nsO3Cn/c6Ru99L1cimjFz7V8izjpU1Kz+XbFr89mrNVew4SRAFrrtJrKrEfD2IPMc\n+FEOVxtiUaRYcO+jTrMsfI3jpSb3Bnlkd/H90W6713whk4J7DcKJiaVHLZtUm5FP\nWABKsiyevzrJvxHVyC4aE0lYzrllVxpKf5xGinwAuY67O7ODAMdFQfvtIkJLp938\nmwXONgxmC9LAc6lBXK4ER4XhF9zWGVdgHFK3i7SdqQbRSCg8XRLKDQquOmIZoSF+\naq1sVz2NfZIEiS2rfDgh6PquTR/WXgS3txpcQmq1fG9a72HM4V1fEDUtAgMBAAEw\nDQYJKoZIhvcNAQEFBQADggEBACYexrHl0hECRAV66UDmSeIw3V1gBR9tqYE9Q3LP\nN0jBZA+hQi1oa5PLqwG3LbIHYRwXLThvBMsUNfsAFLvfMJTbRGan8RqUapEdb3nm\nDNZKHG5Sf2bfzIyIb8GnGDLC47sjVK9+ujQuH/xUjiOsf2c5GNJHyibxgq0G1vQq\ntf00D3SV9AkRsSeBjV8irNHk1J/SALFdSnycT4rgUbuvEb0b9FPaHJBxkjFbrSnV\nAVc9F/lrx5uDFhd+FaRTbQcaQzG0UyyHlEa/kUp7Bclz0KD21Rb7GOglqUGK+UK2\n5AvjVnxazsV0DJzTyRVdJ9QiNqOiPzGvMd1cIxPI5NJQEw0=\n\n\tHello, World!\n \n\n\n\nVerifying a signature with a single key\n---------------------------------------\n\n>>> def verify_file(xml_file, key_file):\n... \"\"\"verify signature in *xml_file* with key in *key_file*.\n... \n... *xml_file* contains the signed XML document.\n... It can be a file, a filename string or an HTTP/FTP url.\n... \n... *key_file* contains the PEM public key. It must be a filename.\n... \n... An exception is raised when the verification fails.\n... \"\"\"\n... from lxml.etree import parse\n... doc = parse(xml_file)\n... node = doc.find(\".//{%s}Signature\" % xmlsec.DSigNs)\n... dsigCtx = xmlsec.DSigCtx()\n... # Note: we do not provide read access to `dsigCtx.signKey`.\n... # Therefore, unlike the `xmlsec` example, we must set the key name\n... # before we assign it to `dsigCtx`\n... signKey = xmlsec.Key.load(key_file, xmlsec.KeyDataFormatPem, None)\n... signKey.name = basename(key_file)\n... # Note: the assignment below effectively copies the key\n... dsigCtx.signKey = signKey\n... dsigCtx.verify(node)\n... \n>>> verify_file(BytesIO(signed_file), BASEDIR + \"rsapub.pem\")\n\n\n\nVerifying a signature with a keys manager\n-----------------------------------------\n\n>>> def load_keys(*keys):\n... \"\"\"return `KeysMngr` with *keys*.\n... \n... *keys* is a sequence of filenames containing PEM encoded keys.\n... \"\"\"\n... mngr = xmlsec.KeysMngr()\n... for k in keys:\n... # must set the key name before we add the key to `mngr`\n... key = xmlsec.Key.load(k, xmlsec.KeyDataFormatPem)\n... key.name = basename(k)\n... # adds a copy of *key*\n... mngr.addKey(key)\n... return mngr\n... \n>>> def verify_file_with_keysmngr(xml_file, mngr):\n... \"\"\"verify *xml_file* with keys manager *mngr*.\n... \n... *xml_file* contains the signed XML document.\n... It can be a file, a filename string or an HTTP/FTP url.\n... \"\"\"\n... from lxml.etree import parse\n... doc = parse(xml_file)\n... node = doc.find(\".//{%s}Signature\" % xmlsec.DSigNs)\n... dsigCtx = xmlsec.DSigCtx(mngr)\n... dsigCtx.verify(node)\n... \n>>> mngr = load_keys(BASEDIR + \"rsapub.pem\")\n>>> verify_file_with_keysmngr(BytesIO(signed_file), mngr)\n\n\nVerifying a signature with X509 certificates\n--------------------------------------------\n\n\n>>> def load_trusted_certs(*certs):\n... \"\"\"return keys manager trusting *certs*.\n... \n... *certs* is a sequence of filenames containing PEM encoded certificates\n... \"\"\"\n... mngr = xmlsec.KeysMngr()\n... for c in certs:\n... mngr.loadCert(c, xmlsec.KeyDataFormatPem, xmlsec.KeyDataTypeTrusted)\n... return mngr\n... \n>>> mngr = load_trusted_certs(BASEDIR + \"rootcert.pem\")\n>>> verify_file_with_keysmngr(BASEDIR + \"sign3-res.xml\", mngr)\n\n\n\nVerifying a signature with additional restrictions\n--------------------------------------------------\n\n>>> def verify_file_with_restrictions(xml_file, mngr):\n... \"\"\"like `verify_file_with_keysmanager` but with restricted signature and reference transforms.\n... \"\"\"\n... from lxml.etree import parse\n... doc = parse(xml_file)\n... node = doc.find(\".//{%s}Signature\" % xmlsec.DSigNs)\n... dsigCtx = xmlsec.DSigCtx(mngr)\n... for allow in \"InclC14N ExclC14N Sha1\".split():\n... tid = getattr(xmlsec, \"Transform%s\" % allow)\n... dsigCtx.enableSignatureTransform(tid)\n... dsigCtx.enableReferenceTransform(tid)\n... dsigCtx.enableSignatureTransform(xmlsec.TransformRsaSha1)\n... dsigCtx.enableReferenceTransform(xmlsec.TransformEnveloped)\n... # thanks to a patch provided by Greg Vishnepolsky, we can know\n... # also limit the acceptable key data\n... dsigCtx.setEnabledKeyData([xmlsec.KeyDataX509])\n... dsigCtx.verify(node)\n... \n>>> # this works\n>>> verify_file_with_restrictions(BASEDIR + \"verify4-res.xml\", mngr)\n>>> # this fails\n>>> verify_file_with_restrictions(BASEDIR + \"verify4-bad-res.xml\", mngr)\nTraceback (most recent call last):\n ...\nError: ('verifying failed with return value', -1)\n>>> # while this works (without the restrictions)\n>>> verify_file_with_keysmngr(BASEDIR + \"verify4-bad-res.xml\", mngr)\n\n\nSigning and verification of binary data\n---------------------------------------\n\nThis use case (which I need for SAML2 support) is not directly\nsupported by ``libxmlsec``. Unlike other examples, the following\nexample has therefore no correspondence with an example for\n``libxmlsec``.\n\n>>> def sign_binary(data, algorithm, key_file):\n... \"\"\"sign binary *data* with *algorithm*, key in *key_file, and return signature.\"\"\"\n... dsigCtx = xmlsec.DSigCtx()\n... dsigCtx.signKey = xmlsec.Key.load(key_file, xmlsec.KeyDataFormatPem, None)\n... return dsigCtx.signBinary(data, algorithm)\n... \n>>> def verify_binary(data, algorithm, key_file, signature):\n... \"\"\"verify *signature* for *data* with *algorithm, key in *key_file*.\"\"\"\n... dsigCtx = xmlsec.DSigCtx()\n... dsigCtx.signKey = xmlsec.Key.load(key_file, xmlsec.KeyDataFormatPem, None)\n... dsigCtx.verifyBinary(data, algorithm, signature)\n... \n>>> bin_data = b\"123\"\n>>> \n>>> # sign\n... # Note: you cannot use a public rsa key for signing.\n... signature = sign_binary(bin_data, xmlsec.TransformRsaSha1, BASEDIR + \"rsakey.pem\")\n>>> \n>>> # verify\n... # Note: you cannot use a private rsa key for verification.\n... verify_binary(bin_data, xmlsec.TransformRsaSha1, BASEDIR + \"rsapub.pem\", signature)\n>>> \n>>> # failing verification\n... verify_binary(bin_data + b\"1\", xmlsec.TransformRsaSha1, BASEDIR + \"rsapub.pem\", signature)\nTraceback (most recent call last):\n ...\ndm.xmlsec.binding._xmlsec.VerificationError: Signature verification failed\n\n\nEncrypting binary data with a template file\n-------------------------------------------\n\n>>> def encrypt_data(tmpl_file, key_file, data):\n... \"\"\"encrypt *data* with key in *key_file* using template in *tmpl_file*.\n... \n... *tmpl_file* actually contains an XML document containing an encryption\n... template. It can be a file, a filename string or an HTTP/FTP url.\n... \n... *key_file* contains a triple DES key. It must be a filename string.\n... \"\"\"\n... from lxml.etree import parse\n... doc = parse(tmpl_file)\n... node = xmlsec.findNode(doc, xmlsec.enc(\"EncryptedData\"))\n... encCtx = xmlsec.EncCtx()\n... # Note: we do not provide read access to `encCtx.encKey`.\n... # Therefore, unlike the `xmlsec` example, we must set the key name\n... # before we assign it to `dsigCtx`\n... encKey = xmlsec.Key.readBinaryFile(xmlsec.KeyDataDes, key_file)\n... encKey.name = basename(key_file)\n... # Note: the assignment below effectively copies the key\n... encCtx.encKey = encKey\n... encCtx.encryptBinary(node, data)\n... return tostring(doc)\n... \n>>> encrypted_data = encrypt_data(BASEDIR + \"encrypt1-tmpl.xml\", BASEDIR + \"deskey.bin\", b\"123\")\n>>> print(to_text(encrypted_data))\n\n \n \n\tdeskey.bin\n \n \n\t...\n \n\n\n\n\nEncrypting xml file with a dynamically created template\n-------------------------------------------------------\n\n>>> def encrypt_file_create_template(xml_file, key_file, type):\n... \"\"\"encrypt *xml_file* with key in *key_file*, generating the template.\n... \n... *xml_file* contains an XML file content of which should be encrypted.\n... It can be a file, a filename string or an HTTP/FTP url.\n... *key_file* contains a triple DES key. It must be a filename string.\n... \"\"\"\n... # template aware infrastructure\n... from dm.xmlsec.binding.tmpl import parse, Element, SubElement, \\\n... fromstring, XML\n... from dm.xmlsec.binding.tmpl import EncData\n... doc = parse(xml_file)\n... encData = EncData(xmlsec.TransformDes3Cbc, type=type)\n... encData.ensureCipherValue() # target for encryption result\n... keyInfo = encData.ensureKeyInfo()\n... encCtx = xmlsec.EncCtx()\n... encKey = xmlsec.Key.readBinaryFile(xmlsec.KeyDataDes, key_file)\n... # must set the key before the key assignment to `encCtx`\n... encKey.name = key_file\n... encCtx.encKey = encKey\n... ed = encCtx.encryptXml(encData, doc.getroot())\n... return tostring(ed.getroottree())\n... \n>>> encrypted_file_element = encrypt_file_create_template(\n... BASEDIR + \"encrypt2-doc.xml\",\n... BASEDIR + \"deskey.bin\",\n... xmlsec.TypeEncElement,\n... )\n>>> print(to_text(encrypted_file_element))\n...\n>>> encrypted_file_content = encrypt_file_create_template(\n... BASEDIR + \"encrypt2-doc.xml\",\n... BASEDIR + \"deskey.bin\",\n... xmlsec.TypeEncContent,\n... )\n>>> print(to_text(encrypted_file_content))\n...\n\n\n\nEncrypting data with a session key\n----------------------------------\n\n>>> def load_rsa_keys(*keys):\n... \"\"\"return `KeysMngr` with *keys*.\n... \n... *keys* is a sequence of key files (given by their filenames) containing\n... PEM encoded RSA keys\n... \"\"\"\n... mngr = xmlsec.KeysMngr()\n... for k in keys:\n... key = xmlsec.Key.load(k, xmlsec.KeyDataFormatPem)\n... key.name = basename(k)\n... mngr.addKey(key)\n... return mngr\n... \n>>> def encrypt_file_with_session_key(mngr, xml_file, key_name):\n... \"\"\"encrypt *xml_file* with encrypted session key.\n... \n... The template is dynamically created.\n... \n... The session key is encrypted with a key managed by *mngr* under\n... name *key_name*.\n... \"\"\"\n... # template aware infrastructure\n... from dm.xmlsec.binding.tmpl import parse, Element, SubElement, \\\n... fromstring, XML\n... from dm.xmlsec.binding.tmpl import EncData\n... doc = parse(xml_file)\n... encData = EncData(xmlsec.TransformDes3Cbc, type=xmlsec.TypeEncElement)\n... encData.ensureCipherValue() # target for encryption result\n... keyInfo = encData.ensureKeyInfo()\n... encKey = keyInfo.addEncryptedKey(xmlsec.TransformRsaPkcs1)\n... encKey.ensureCipherValue()\n... encKeyInfo = encKey.ensureKeyInfo()\n... encKeyInfo.addKeyName(key_name)\n... encCtx = xmlsec.EncCtx(mngr)\n... encCtx.encKey = xmlsec.Key.generate(xmlsec.KeyDataDes, 192, xmlsec.KeyDataTypeSession)\n... ed = encCtx.encryptXml(encData, doc.getroot())\n... return tostring(ed.getroottree())\n... \n>>> mngr = load_rsa_keys(BASEDIR + \"rsakey.pem\")\n>>> print(to_text(encrypt_file_with_session_key(\n... mngr,\n... BASEDIR + \"encrypt3-doc.xml\",\n... \"rsakey.pem\",\n... )))\nrsakey.pem......\n\n\n\nDecrypting data with a single key\n---------------------------------\n\n>>> def decrypt_file(enc_file, key_file):\n... \"\"\"decrypt *enc_file* with key in *key_file*.\n... \n... *enc_file* contains the encrypted XML document.\n... It can be a file, a filename string or an HTTP/FTP url.\n... \n... *key_file* contains the triple DES encryption key. It must be a filename.\n... \n... The decrypted data is returned.\n... \"\"\"\n... from lxml.etree import parse, _Element\n... doc = parse(enc_file)\n... node = xmlsec.findNode(doc, xmlsec.enc(\"EncryptedData\"))\n... encCtx = xmlsec.EncCtx()\n... # Note: we do not provide read access to `encCtx.encKey`.\n... # Therefore, unlike the `xmlsec` example, we must set the key name\n... # before we assign it to `dsigCtx`\n... encKey = xmlsec.Key.readBinaryFile(xmlsec.KeyDataDes, key_file)\n... encKey.name = basename(key_file)\n... # Note: the assignment below effectively copies the key\n... encCtx.encKey = encKey\n... dr = encCtx.decrypt(node)\n... if isinstance(dr, _Element):\n... # decrypted xml data\n... return tostring(dr.getroottree())\n... else:\n... # decrypted binary data\n... return dr\n... \n>>> to_text(decrypt_file(BytesIO(encrypted_data), BASEDIR + \"deskey.bin\"))\n'123'\n\n\n\nDecrypting data with a keys manager\n-----------------------------------\n\n>>> def load_des_keys(*keys):\n... \"\"\"return keys manager with *keys*.\n... \n... *keys* is a sequence a key files (given by their filenames) containing\n... binary des keys.\n... \"\"\"\n... from os.path import basename\n... mngr = xmlsec.KeysMngr()\n... for k in keys:\n... key = xmlsec.Key.readBinaryFile(xmlsec.KeyDataDes, k)\n... key.name = basename(k)\n... mngr.addKey(key)\n... return mngr\n... \n>>> def decrypt_file_with_keys_manager(mngr, enc_file):\n... \"\"\"decrypt the encrypted *enc_file* by keys managed by *mngr*.\"\"\"\n... from lxml.etree import parse, _Element\n... doc = parse(enc_file)\n... encData = xmlsec.findNode(doc, xmlsec.enc(\"EncryptedData\"))\n... encCtx = xmlsec.EncCtx(mngr)\n... dr = encCtx.decrypt(encData)\n... if isinstance(dr, _Element):\n... # decrypted XML\n... return tostring(dr.getroottree())\n... else:\n... # decrypted binary data\n... return dr\n... \n>>> mngr = load_des_keys(BASEDIR + \"deskey.bin\")\n>>> print(to_text(decrypt_file_with_keys_manager(mngr, BASEDIR + \"encrypt1-res.xml\")))\nBig secret\n>>> print(to_text(decrypt_file_with_keys_manager(mngr, BASEDIR + \"encrypt2-res.xml\")))\n\n \n Hello, World!\n \n\n>>> print(to_text(decrypt_file_with_keys_manager(mngr, BytesIO(encrypted_file_element))))\n\n \n\tHello, World!\n \n\n>>> print(to_text(decrypt_file_with_keys_manager(mngr, BytesIO(encrypted_file_content))))\n\n \n\tHello, World!\n \n\n\n\n\nObtaining error information\n---------------------------\n\n``xmlsec`` is quite terse with error information. Its functions return\n``-1`` or ``NULL`` on error and that's what you get via the API.\nIn case of an error, ``xmlsec`` reports information resembling a traceback\nvia the ``libxml2`` error reporting mechanism. However, ``lxml`` does\nnot initialize the mechanism and the resulting reports are lost.\n\nFortunately, ``xmlsec`` allows its error reporting mechanism to\nbe overridden and this binding does it in a way that you can\ncustomize it. The following example shows how:\n\n>>> def print_errors(filename, line, func, errorObject, errorSubject, reason, msg):\n... # this would give complete but often not very usefull) information\n... # print(\"%(filename)s:%(line)d(%(func)s) error %(reason)d obj=%(errorObject)s subject=%(errorSubject)s: %(msg)s\" % locals())\n... # the following prints if we get something with relation to the application\n... info = []\n... if errorObject != \"unknown\": info.append(\"obj=\" + errorObject)\n... if errorSubject != \"unknown\": info.append(\"subject=\" + errorSubject)\n... if msg.strip(): info.append(\"msg=\" + msg)\n... # see `xmlsec`s `errors.h`for the meaning\n... if reason != 1: info.append(\"errno=%d\" % reason)\n... if info:\n... print(\"%s:%d(%s)\" % (filename, line, func), \" \".join(info))\n... \n>>> xmlsec.set_error_callback(print_errors)\n\nThis installs ``print_errors`` as error reporting hook.\nWe now repeat the example \"Verify signature with additional restrictions\"\nto see what the error report tells us.\n\n>>> verify_file_with_restrictions(BASEDIR + \"verify4-bad-res.xml\", mngr) # doctest: +SKIP\ntransforms.c:1546(xmlSecTransformNodeRead) subject=xpath msg=href=http://www.w3.org/TR/1999/REC-xpath-19991116\ntransforms.c:733(xmlSecTransformCtxNodesListRead) subject=xmlSecTransformNodeRead msg=node=Transform\nxmldsig.c:1454(xmlSecDSigReferenceCtxProcessNode) subject=xmlSecTransformCtxNodesListRead msg=node=Transforms\nxmldsig.c:804(xmlSecDSigCtxProcessSignedInfoNode) subject=xmlSecDSigReferenceCtxProcessNode msg=node=Reference\nxmldsig.c:547(xmlSecDSigCtxProcessSignatureNode) subject=xmlSecDSigCtxProcessSignedInfoNode\nxmldsig.c:366(xmlSecDSigCtxVerify) subject=xmlSecDSigCtxSigantureProcessNode\nTraceback (most recent call last):\n ...\ndm.xmlsec.binding._xmlsec.Error: ('verifying failed with return value', -1)\n\nAs before, we get an exception for the failing verification. But, now,\nwe have in addition the traceback like error information from ``xmlsec``.\nWith some ingenuity, we can deduce that there is some problem\nwith the \"xpath\" transform. Up to us to recognize that we have not\nenabled this transform.\n\nAs you see, even with the error information, it might be quite difficult\nto understand problems. In difficult cases, it might be necessary\nto obtain the ``xmlsec`` source code and learn what is happening\nin the error context.\n\nNote that the numbers in the error output are source code line numbers.\nThey depend on the ``xmlsec`` version you have installed and\nconsequently can be different when you try this code.\n\nIf the error information contains ``errno`` (``reason`` at the base\ninterface), then these numbers refer to the error numbers defined\nin the ``errors.h`` of ``libxmlsec``. As this file is a prerequisite\nfor the installation of this package, it is likely installed on your system\n(unlike the ``libxmlsec`` sources). You may be able to guess from the\nerror name what went wrong, which sometimes avoids downloading\nthe full sources.\n\n\n\nNotes\n=====\n\nXML ids\n-------\n\nDigital signatures and XML encryption can make use of XML ids. For example,\nthis is the case for SAML2. XML ids can make problems as XML does not\nspecify which attributes may contain an id. Newer versions of XML designated\n``xml:id`` for this purpose, but older standards (again SAML2 is an\nexample) does not yet use this but their own id attributes.\nAs a consequence, the XML processing system (``libxml2`` in our context)\nmust be informed about which attributes can contain ids.\n\n``libxml2`` knows about ``xml:id`` and if the XML document is\nvalidated against a document type or an XML schema, it uses the information\ndescribed there to identify id attributes. If the XML document\nis not validated, any id attributes different from ``xml:id``\nmust be made known by the application through a call to\nthe ``addIds(node, ids)`` function defined by ``xmlsec``.\n``addIds`` visits the children of *node* (probably recursively)\nand extends the id map (it maps ids to nodes) of the document of *node*\nfor each found attribute whose name is listed in *ids* (a list of\nattribute names).\n\nNote that the error information provided by ``xmlsec`` in case\nof an undeclared id attribute may be difficult to decipher. It will probably\ntell you about a problem with an XPointer transform in this case.\n\nNote also that id references may be made indirectly, e.g. via\nfragment parts of urls (again, SAML2 is an example). Thus,\nwhen signing, signature verification or encryption/decryption\nfails for no apparent reason it may be a good idea to check\nwhether this might be caused by unknown id attribute information.\n\n\nHistory\n=======\n\n2.0\n Python 3 (3.3+) support\n\n1.3.7\n Fix `EncData` template bug reported by Jorge Romero.\n\n1.3.6\n Fix a bug reported by Jorge Romero: encrypt content failed if the first\n content node was not an element.\n\n1.3.4\n Suppressed `xmlsec` initialization in `tests.txt`: Apparently, newer\n versions of `libxmlsec` check against repeated initialization, which\n let the tests fail. For this to work, the `README.txt` tests must\n come before those in `tests.txt`. I am not sure that this is guaranteed;\n potentially, a more elaborate technique to avoid repeated initialization\n is necessary.\n\n1.3.3\n Applied patch provided by Robert Frije to make the `nsPrefix` template\n parameter work as expected.\n\n1.3.2\n Workaround for ``buildout`` problem (not honoring version pinning\n for ``setup_requires`` dependencies).\n\n1.3\n Support for digital signatures of binary data\n\n Improved transform support\n\n1.2\n Greg Vishnepolsky provided support for ``DSigCtx.setEnabledKeyData``.\n\n1.1\n for lxml 3.x\n\n1.0\n for lxml 2.x",
"description_content_type": "",
"docs_url": null,
"download_url": "",
"downloads": {
"last_day": -1,
"last_month": -1,
"last_week": -1
},
"home_page": "https://pypi.org/project/dm.xmlsec.binding",
"keywords": "encryption xml security digital signature cython lxml",
"license": "BSD",
"maintainer": "",
"maintainer_email": "",
"name": "dm.xmlsec.binding",
"package_url": "https://pypi.org/project/dm.xmlsec.binding/",
"platform": "",
"project_url": "https://pypi.org/project/dm.xmlsec.binding/",
"project_urls": {
"Homepage": "https://pypi.org/project/dm.xmlsec.binding"
},
"release_url": "https://pypi.org/project/dm.xmlsec.binding/2.0/",
"requires_dist": null,
"requires_python": "",
"summary": "Cython/lxml based binding for the XML security library -- for lxml 3.x",
"version": "2.0"
},
"last_serial": 4482094,
"releases": {
"1.0": [
{
"comment_text": "",
"digests": {
"md5": "29bd07bd877ef67b650c0ec14dfb169c",
"sha256": "b8a8748d8abcad33f1e1c772e115187efaf502c95daf7658f8fe5982e7d9504f"
},
"downloads": -1,
"filename": "dm.xmlsec.binding-1.0.tar.gz",
"has_sig": false,
"md5_digest": "29bd07bd877ef67b650c0ec14dfb169c",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 113261,
"upload_time": "2015-01-20T10:45:30",
"url": "https://files.pythonhosted.org/packages/8d/3d/532e3b1279c326c2b30a153ab8bf6a17787cce123aa10d7496eceacd515f/dm.xmlsec.binding-1.0.tar.gz"
}
],
"1.0b1": [
{
"comment_text": "",
"digests": {
"md5": "0a85849845d51bd746aa5170dbe3d956",
"sha256": "bf03c8a8f867bdc1b48dcdfa13bdb5a468533a88a0c55e3c3f6a5a50eacfcc1a"
},
"downloads": -1,
"filename": "dm.xmlsec.binding-1.0b1.tar.gz",
"has_sig": false,
"md5_digest": "0a85849845d51bd746aa5170dbe3d956",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 107369,
"upload_time": "2012-07-02T10:12:29",
"url": "https://files.pythonhosted.org/packages/1b/da/b7102e92ed35b4ab8048a666ed122b00e51490eaa8b73a0faaf6444e8358/dm.xmlsec.binding-1.0b1.tar.gz"
}
],
"1.0b2": [
{
"comment_text": "",
"digests": {
"md5": "b8f5e1e5ad7ea1edc0d54acef87bfb6e",
"sha256": "887b9a94a9e52f8816fbc8160e3dd6a38dff86ceb0f41297ab060738f7c087a9"
},
"downloads": -1,
"filename": "dm.xmlsec.binding-1.0b2.tar.gz",
"has_sig": false,
"md5_digest": "b8f5e1e5ad7ea1edc0d54acef87bfb6e",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 108403,
"upload_time": "2012-07-02T14:26:39",
"url": "https://files.pythonhosted.org/packages/82/d6/d82e1acbafe3e1f04cf455beaeff841dadb4f106f4337fe4938f5f590654/dm.xmlsec.binding-1.0b2.tar.gz"
}
],
"1.0b3": [
{
"comment_text": "",
"digests": {
"md5": "0bd82820c6ef70d3eb8075256d4620b3",
"sha256": "2bd05b3e2bec8df8ba88cd92896e67e6e70ee262e504df3c4abde15d20c0de7f"
},
"downloads": -1,
"filename": "dm.xmlsec.binding-1.0b3.tar.gz",
"has_sig": false,
"md5_digest": "0bd82820c6ef70d3eb8075256d4620b3",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 109079,
"upload_time": "2012-07-08T08:21:06",
"url": "https://files.pythonhosted.org/packages/64/b5/c8480e6bd3b074d35e6ed57e0d6c5c4548b1617375d8b02fdee8792f9e2c/dm.xmlsec.binding-1.0b3.tar.gz"
}
],
"1.0b4": [
{
"comment_text": "",
"digests": {
"md5": "bef9abec4a046e47f35b974e48916b3b",
"sha256": "a61aa58f5f9ea03e1a3b0770ac20d3c68dd0576b65471b4c6362b13257de4bf3"
},
"downloads": -1,
"filename": "dm.xmlsec.binding-1.0b4.tar.gz",
"has_sig": false,
"md5_digest": "bef9abec4a046e47f35b974e48916b3b",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 112947,
"upload_time": "2013-01-09T08:46:59",
"url": "https://files.pythonhosted.org/packages/71/d3/57240451cffef0eb29b6d7152ea6acad47faeabb12f5fd5e5a77265df789/dm.xmlsec.binding-1.0b4.tar.gz"
}
],
"1.1b1": [
{
"comment_text": "",
"digests": {
"md5": "22d15886d46087e411a03f4bedb8842d",
"sha256": "9fa6d12d6feb6fc24a39228852a0d4bcf7942dd114e538f97cc9cc510df00bff"
},
"downloads": -1,
"filename": "dm.xmlsec.binding-1.1b1.tar.gz",
"has_sig": false,
"md5_digest": "22d15886d46087e411a03f4bedb8842d",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 109813,
"upload_time": "2013-01-09T09:54:19",
"url": "https://files.pythonhosted.org/packages/79/7e/a83c8fb900277d266285bee5a6f9c7111f39d6bb913eb863bcfb1dc74f01/dm.xmlsec.binding-1.1b1.tar.gz"
}
],
"1.2": [
{
"comment_text": "",
"digests": {
"md5": "9a893e8c45d68561b5d045e58d0b6630",
"sha256": "ce86afe24305ed832a4453c148810d96c56a79cef980ca29e5a4a16a80009610"
},
"downloads": -1,
"filename": "dm.xmlsec.binding-1.2.tar.gz",
"has_sig": false,
"md5_digest": "9a893e8c45d68561b5d045e58d0b6630",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 111514,
"upload_time": "2013-11-11T10:58:57",
"url": "https://files.pythonhosted.org/packages/43/c0/4d8105a00acdb1992743578a0c07417c628258d2e5d79ecc57164ab10339/dm.xmlsec.binding-1.2.tar.gz"
}
],
"1.3": [
{
"comment_text": "",
"digests": {
"md5": "8a35f35935a87935601c6301799a8a66",
"sha256": "5c99289fd0cde8436927c119438e118b5b53814e349c2e33bed7bdf883bc0bee"
},
"downloads": -1,
"filename": "dm.xmlsec.binding-1.3.tar.gz",
"has_sig": false,
"md5_digest": "8a35f35935a87935601c6301799a8a66",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 119444,
"upload_time": "2014-08-21T06:26:51",
"url": "https://files.pythonhosted.org/packages/52/c1/69d5a6d0a7f80e834c7da3116a8dd8f2a1922c191b15c5f841c89dc3ee49/dm.xmlsec.binding-1.3.tar.gz"
}
],
"1.3.1": [
{
"comment_text": "",
"digests": {
"md5": "4e25218cd5826415b94a25ad6e76cc79",
"sha256": "0d8ef03c5bcb52b4c0913ecacb3c3375636f4d64a5a8ca767644a2b78ddd96b9"
},
"downloads": -1,
"filename": "dm.xmlsec.binding-1.3.1.tar.gz",
"has_sig": false,
"md5_digest": "4e25218cd5826415b94a25ad6e76cc79",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 119460,
"upload_time": "2014-09-06T06:16:51",
"url": "https://files.pythonhosted.org/packages/4a/80/69b71cba705ac204d73c16f2c74fdddd166ff37b2f0e845a18157bf5c756/dm.xmlsec.binding-1.3.1.tar.gz"
}
],
"1.3.2": [
{
"comment_text": "",
"digests": {
"md5": "3c3bfd3569b139c7960c32f9116b7773",
"sha256": "b2f06d323b9d7cd3d1b5848f08a83627662ecf15369dcc3464f37d04917d9f7c"
},
"downloads": -1,
"filename": "dm.xmlsec.binding-1.3.2.tar.gz",
"has_sig": false,
"md5_digest": "3c3bfd3569b139c7960c32f9116b7773",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 119962,
"upload_time": "2015-01-10T08:48:50",
"url": "https://files.pythonhosted.org/packages/0c/47/c8e17a5fec7bc86b092ba6841c2db7ce5e828327eb72d6a8c4c93a19f62e/dm.xmlsec.binding-1.3.2.tar.gz"
}
],
"1.3.3": [
{
"comment_text": "",
"digests": {
"md5": "530b28b6ca044e358ad757acbc5d90af",
"sha256": "1913cd464c44369daf495b23748d5d9d42ea0a83a44c4c6d34d2a1a56aa68ded"
},
"downloads": -1,
"filename": "dm.xmlsec.binding-1.3.3.tar.gz",
"has_sig": false,
"md5_digest": "530b28b6ca044e358ad757acbc5d90af",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 120120,
"upload_time": "2017-05-31T06:32:18",
"url": "https://files.pythonhosted.org/packages/02/ba/b534536eb50bc013cd8dc3116c46eee4bff5b9e57e2af23f81afc574a67a/dm.xmlsec.binding-1.3.3.tar.gz"
}
],
"1.3.5": [
{
"comment_text": "",
"digests": {
"md5": "fef162e9c7fcc5210fc240f44f621195",
"sha256": "1a240a11aa37c7e624bd97ee4e49d58f4be6202fba23a524a4f6f72bcc78d9e7"
},
"downloads": -1,
"filename": "dm.xmlsec.binding-1.3.5.tar.gz",
"has_sig": false,
"md5_digest": "fef162e9c7fcc5210fc240f44f621195",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 121194,
"upload_time": "2018-07-11T16:59:28",
"url": "https://files.pythonhosted.org/packages/58/fa/aebfade479749290048d0c0139da92b95038a0d3a37b782a982bf5d2f99c/dm.xmlsec.binding-1.3.5.tar.gz"
}
],
"1.3.6": [
{
"comment_text": "",
"digests": {
"md5": "7f5fdbbc7a12bc650263d7ffc820a976",
"sha256": "96b2ad3968e69cd6aa20fe5c4e2d43cf5ab274ac1d71bf71911effd6f48a18f1"
},
"downloads": -1,
"filename": "dm.xmlsec.binding-1.3.6.tar.gz",
"has_sig": false,
"md5_digest": "7f5fdbbc7a12bc650263d7ffc820a976",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 145829,
"upload_time": "2018-07-28T16:23:08",
"url": "https://files.pythonhosted.org/packages/16/93/a8382f8ac2ac07b1d474a4fe43b0b05cf65d04239d72f84812c53f0049c2/dm.xmlsec.binding-1.3.6.tar.gz"
}
],
"1.3.7": [
{
"comment_text": "",
"digests": {
"md5": "b00f23a9dbe3235eaf2af54f18490c80",
"sha256": "634d59acd07e5787b29f3f39e00c0b7f610154b48947de00b3e2dfd09996520e"
},
"downloads": -1,
"filename": "dm.xmlsec.binding-1.3.7.tar.gz",
"has_sig": false,
"md5_digest": "b00f23a9dbe3235eaf2af54f18490c80",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 145862,
"upload_time": "2018-07-31T05:08:59",
"url": "https://files.pythonhosted.org/packages/2c/9e/7651982d50252692991acdae614af821fd6c79bc8dcd598ad71d55be8fc7/dm.xmlsec.binding-1.3.7.tar.gz"
}
],
"2.0": [
{
"comment_text": "",
"digests": {
"md5": "8a8c80b0642e59727cfad360e269ce84",
"sha256": "0b07d083a763b894c0e142b086e9739671babd803361313a0f86d956930553eb"
},
"downloads": -1,
"filename": "dm.xmlsec.binding-2.0.tar.gz",
"has_sig": false,
"md5_digest": "8a8c80b0642e59727cfad360e269ce84",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 148876,
"upload_time": "2018-11-13T15:14:15",
"url": "https://files.pythonhosted.org/packages/56/30/7d19e02398b46593c7a72589c767c50f1ff4a96845f5bc6c61c3183fb213/dm.xmlsec.binding-2.0.tar.gz"
}
]
},
"urls": [
{
"comment_text": "",
"digests": {
"md5": "8a8c80b0642e59727cfad360e269ce84",
"sha256": "0b07d083a763b894c0e142b086e9739671babd803361313a0f86d956930553eb"
},
"downloads": -1,
"filename": "dm.xmlsec.binding-2.0.tar.gz",
"has_sig": false,
"md5_digest": "8a8c80b0642e59727cfad360e269ce84",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 148876,
"upload_time": "2018-11-13T15:14:15",
"url": "https://files.pythonhosted.org/packages/56/30/7d19e02398b46593c7a72589c767c50f1ff4a96845f5bc6c61c3183fb213/dm.xmlsec.binding-2.0.tar.gz"
}
]
}