diff --git a/doc/index.rst b/doc/index.rst index 14dbb5f..bad4d43 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -100,9 +100,9 @@ list of ``plugins`` passed to a new ``Client`` instance:: ) ``WssePlugin`` requires that the outgoing messages already have a -``wsse:Security`` element in the ``soap:Header`` with a ``wsu:Timestamp`` -token. Suds can do this via its ``Security`` and ``Timestamp`` objects, as -shown in the above example. +``wsse:Security`` element in the ``soap:Header`` with an optional +``wsu:Timestamp`` token. Suds can do this via its ``Security`` and +``Timestamp`` objects, as shown in the above example. In the example, ``our_keyfile_path``, ``our_certfile_path``, and ``their_certfile_path`` should all be absolute filesystem paths to X509 @@ -112,8 +112,9 @@ used to encrypt outgoing messages and verify the signature on incoming messages. Note that ``WssePlugin`` is currently hardcoded to sign the ``wsu:Timestamp`` -and ``soap:Body`` elements, and to encrypt only the first child of the -``soap:Body`` element. Pull requests to add more flexibility are welcome. +(if it is present) and ``soap:Body`` elements, and to optionally encrypt only +the first child of the ``soap:Body`` element. +Pull requests to add more flexibility are welcome. Standalone functions diff --git a/wsse/signing.py b/wsse/signing.py index 22beac7..321aa54 100644 --- a/wsse/signing.py +++ b/wsse/signing.py @@ -20,7 +20,7 @@ def sign(envelope, keyfile, certfile): """Sign given SOAP envelope with WSSE sig using given key and cert. Sign the wsu:Timestamp node in the wsse:Security header and the soap:Body; - both must be present. + Timestamp is optional, Body must be present. Add a ds:Signature node in the wsse:Security header containing the signature. @@ -135,7 +135,8 @@ def sign(envelope, keyfile, certfile): ctx = xmlsec.SignatureContext() ctx.key = key _sign_node(ctx, signature, doc.find(ns(SOAP_NS, 'Body'))) - _sign_node(ctx, signature, security.find(ns(WSU_NS, 'Timestamp'))) + if security.find(ns(WSU_NS, 'Timestamp')): + _sign_node(ctx, signature, security.find(ns(WSU_NS, 'Timestamp'))) ctx.sign(signature) # Place the X509 data inside a WSSE SecurityTokenReference within diff --git a/wsse/suds.py b/wsse/suds.py index 3fe1d11..5a07984 100644 --- a/wsse/suds.py +++ b/wsse/suds.py @@ -2,17 +2,22 @@ from __future__ import absolute_import from suds.plugin import MessagePlugin +from logging import getLogger + from .encryption import encrypt, decrypt from .signing import sign, verify +log = getLogger(__name__) + class WssePlugin(MessagePlugin): """Suds message plugin that performs WS-Security signing and encryption. - Encrypts and signs outgoing messages (the soap:Body and the wsu:Timestamp - security token, which must be present); decrypts and verifies signature on - incoming messages. + Encrypts (optional) and signs outgoing messages (the soap:Body and the + wsu:Timestamp security token, which must be present); decrypts and verifies + signature on incoming messages. + Encryption is done if their_certfile is set. Uses X509 certificates for both encryption and signing. Requires our cert and its private key, and their cert (all as file paths). @@ -39,19 +44,30 @@ class WssePlugin(MessagePlugin): only the first child element of the soap:Body will be encrypted). """ - def __init__(self, keyfile, certfile, their_certfile): + def __init__(self, keyfile, certfile, their_certfile=None): + """ + @param keyfile path to the private key to sign the content + @param certfile path to the certificate to sign the content + @param their_certfile Optional, path to the recipient certificate to + encrypt, if not set no encryption is done + """ self.keyfile = keyfile self.certfile = certfile self.their_certfile = their_certfile + log.info("WSSE plugin initialized") def sending(self, context): """Sign and encrypt outgoing message envelope.""" context.envelope = sign( context.envelope, self.keyfile, self.certfile) - context.envelope = encrypt(context.envelope, self.their_certfile) + if self.their_certfile: + log.debug("Encrypt the body") + context.envelope = encrypt(context.envelope, self.their_certfile) def received(self, context): """Decrypt and verify signature of incoming reply envelope.""" if context.reply: - context.reply = decrypt(context.reply, self.keyfile) + if self.their_certfile: + log.debug("Decrypt the body") + context.reply = decrypt(context.reply, self.keyfile) verify(context.reply, self.their_certfile)