1

I am trying to connect to a SOAP dataservice that requires both WS-Addressing and WS-Security. So far, I have managed to get a request with a signed body and a signed timestamp. But I also need to add signatures for the wsa:To, wsa:MessageID and wsa:Action elements. I am using the python zeep module.

I found two helpful classes in the issues on zeep's GitHub to help my along. (There is a space after each http: to prevent the urls from triggering the SO spam filter).

class BinarySignatureTimestamp(BinarySignature):
    def apply(self, envelope, headers):
        security = utils.get_security_header(envelope)

        created = datetime.utcnow()
        expired = created + timedelta(seconds=1 * 60)

        timestamp = utils.WSU('Timestamp')
        timestamp.append(utils.WSU('Created', created.replace(microsecond=0).isoformat()+'Z'))
        timestamp.append(utils.WSU('Expires', expired.replace(microsecond=0).isoformat()+'Z'))

        security.append(timestamp)

        super().apply(envelope, headers)
        return envelope, headers

# Somehow, using the WsAddressingPlugin gives me duplicate wsa:To/Action/MessageID elements
#   this little plugin gets rid of the original ones
# I need the WsAddressingPlugin so I can specify a wsa:To address that's different from the url to send the request to. 
class RemoveWSA(Plugin):
    def egress(self, envelope, http_headers, operation, binding_options):
        env = envelope.find('{http: //schemas.xmlsoap.org/soap/envelope/}Header')  
        for child in env:
            if child.tag.startswith('{http: //www.w3.org/2005/08/addressing}'):
                env.remove(child)
        return envelope, http_headers

Then this is my code

session = Session()
session.cert = ('client.pem', 'privkey.pem')
transport = Transport(session=session, cache=SqliteCache())
settings = Settings(strict=False, xml_huge_tree=True)

client = Client('http: //schemas.kvk.nl/contracts/kvk/dataservice/catalogus/2015/02/KVK-KvKDataservice.wsdl', 
  settings=settings,
  transport=transport,
  plugins=[RemoveWSA(), WsAddressingPlugin(address_url = 'http: //es.kvk.nl/kvk-DataservicePP/2015/02')], #  this is the wsa:To address
  wsse=BinarySignatureTimestamp(
    "privkey.pem", 
    "client.pem",
    "certificate_password"))

# the wsdl has example.com as address, which needs to be replaced with the actual url the request needs to go to
client.service._binding_options["address"] = 'https://webservices.preprod.kvk.nl/postbus1'

request_data = {"klantreferentie": "", "kvkNummer": "90003128"}

client.service.ophalenInschrijving(**request_data)

How can I add a signature for the wsa:To, wsa:MessageID and wsa:Action elements to this request? These signature blocks look like this:

<ds:Reference URI="#id-094973EF5ECC133BCB1679398962130734">
  <ds:Transforms>
    <ds:Transform Algorithm="http: //www.w3.org/2001/10/xml-exc-c14n#">
      <ec:InclusiveNamespaces PrefixList="ns wsa" xmlns:ec="http: //www.w3.org/2001/10/xml-exc-c14n#"/>
    </ds:Transform>
  </ds:Transforms>
  <ds:DigestMethod Algorithm="http: //www.w3.org/2000/09/xmldsig#sha1"/>
  <ds:DigestValue>EPWWh6fOlEYHPoQVXaHYC6Ty6GQ=</ds:DigestValue>
</ds:Reference>

Any help is greatly appreciated!

KSchouten
  • 11
  • 2
  • have you ever found a solution? – garma Jun 08 '23 at 17:35
  • 1
    In the end, I forked the python zeep package and with some effort I was able to make the necessary adjustments in there. If you want, you can have a look at my adjustments here: https://github.com/KSchouten/python-zeep – KSchouten Jun 12 '23 at 11:03
  • Thanks; I did it similarly. For future readers you don’t actually need to fork the zeep package you can just copy the signature.py file and make the necessary adjustments – garma Jun 13 '23 at 13:20

0 Answers0