Python client for web services using WS-Security

Hopefully this entry serves as some decent documentation on how to write a Python client that accesses a web service which uses WS-Security. When I was trying to figure it out, Otu Ekanem’s response on the mailing list was invaluable. The example is relevant for any web service framework independent of programming language. This is tested with XFire 1.2.4 but can be used with .NET or other Java web service frameworks like Axis2.

When accessing a web service which has WS-Security enabled you must send very specific headers as part of your SOAP envelope in order for the request to be processed. You can read all about the glorious specification in PDF Format if you like. I’m using the Zolera Soap Infrastructure (ZSI) Library for Python which supports client stub generation. Given the generated stubs, there are two ways of adding custom headers to outgoing SOAP messages.

Method 1 – Not desirable but worth a mention

The first method involves modifying the generated code which is highly undesirable. Using the very simple SportsService web service example, you must modify the generated and edit the following line:

self.binding.Send(None, None,
   request, soapaction="", **kw)

to read

self.binding.Send(None, None,
   request, soapaction="", soapheaders=(obj1,obj2) )

where obj1 and obj2 are instances of Python objects which are serialized as part of the SOAP header. I found this way to be tedious as you have to design your classes to match the SOAP header and write additional serialization code. It is also hard to create the exact header as namespaces and prefixes tend to be a problem.

Method 2 – Probably the way to go, way more customizable

We can use DOM-like methods to modify the SOAP header and send out exactly what we need. The example implements the UsernameToken strategy but other ones can also be implemented by modifying the headers in a similar manner. The generated Port class’ binding attribute has a sig_handler attribute which can be assigned an instance of a custom class. In this custom class, we must implement two methods, sign and verify, that can modify the header and check it’s validity, respectively. The sign method takes in as argument a SoapWriter which enables us to modify the header. So without further ado, here’s the class that adds WS-Security headers to the outgoing SOAP envelope as discussed above. The code has been formatted and modified to fit the page.

# Deprecated in 2.5, use the hashlib module instead:
import sha

import binascii
import base64
import time
import random

class SignatureHandler:

    "" +


  def __init__(self, user, password, useDigest=False):
    self._user = user
    self._created = time.strftime('%Y-%m-%dT%H:%M:%SZ',
    self._nonce =
    if (useDigest):
      self._passwordType = self.PASSWORD_DIGEST_TYPE
      digest = + self._created +

      # binascii.b2a_base64 adds a newline at the end
      self._password = binascii.b2a_base64(digest)[:-1]
      self._passwordType = self.PASSWORD_PLAIN_TYPE
      self._password = password

  def sign(self,soapWriter):

    # create  element
    securityElem = soapWriter._header.
      createAppendElement("", "wsse:Security")
      setAttribute("xmlns:wsse", self.SEC_NS)
      setAttribute("SOAP-ENV:mustunderstand", "1")

    # create  element
    usernameTokenElem = securityElem.
      createAppendElement("", "wsse:UsernameToken")
      setAttribute("xmlns:wsse", self.SEC_NS)
      setAttribute("xmlns:wsu", self.UTIL_NS)

    # create  element
    usernameElem = usernameTokenElem.
      createAppendElement("", "wsse:Username")
      setAttribute("xmlns:wsse", self.SEC_NS)

    # create  element
    passwordElem = usernameTokenElem.
      createAppendElement("", "wsse:Password")
      setAttribute("xmlns:wsse", self.SEC_NS)
      setAttribute("Type", self._passwordType)

    # create  element
    nonceElem = usernameTokenElem.
      createAppendElement("", "wsse:Nonce")
      setAttribute("xmlns:wsse", self.SEC_NS)

    # create  element
    createdElem = usernameTokenElem.
      createAppendElement("", "wsse:Created")
      setAttribute("xmlns:wsse", self.UTIL_NS)

    # put values in elements
    # binascii.b2a_base64 adds a newline at the end

  def verify(self,soapWriter):

Example usage of this is:

from SportsService_client import *
from SportsService_types import *

locator = SportsServiceLocator()
port = locator.getSportsServiceHttpPort()
sigHandler = SignatureHandler("user", "password", True)
port.binding.sig_handler = sigHandler

request = getMascotRequest()
teamObj = ns0.Team_Def("Team")
teamObj._name = "toronto"
request._team = teamObj

response = port.getMascot(request)
print response._out._name

As you can see the SignatureHandler class is implementing an “interface” which enables it to process outgoing SOAP Requests. The verify method is empty but can contain code to check whether the SOAP header is valid.

If you would like write a PHP client that accesses a WS-Security enabled service, you should read Kim Cameron’s IdentityBlog entry which has links to the source code needed. If you simply want to use a PHP client for a non WS-Security web service, an earlier blog entry covers that.


8 thoughts on “Python client for web services using WS-Security

  1. Pingback: il maestro ignoto

  2. Amrlafi

    Need a favor , how to generate this header according to the 2nd method

    username here
    password here

    much appreciated !


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s