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!