UsernameToken with Timestamp token || Python || WS-Security (WSSE)
I should recreate this part of the payload using python.
<wsse:Security soapenv:mustUnderstand="1" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsu:Timestamp wsu:Id="TS-C85E4BAAC54A3C164416475054038092">
<wsu:Created>2022-03-17T08:23:23.809Z</wsu:Created>
<wsu:Expires>2022-03-17T08:24:23.809Z</wsu:Expires>
</wsu:Timestamp>
<wsse:UsernameToken wsu:Id="UsernameToken-C85E4BAAC54A3C164416475053981971">
<wsse:Username>XXXXXXXXXXXXXXX</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">XXXXXXXXXXXXXXXXXXXXXXXX</wsse:Password>
<wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">zL/iJlH2YPm83y+t0wd3Dw==</wsse:Nonce>
<wsu:Created>2022-03-17T08:23:18.195Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
I thought about using this piece of code that I found in the documentation
`>>> import datetime
>>> from zeep import Client
>>> from zeep.wsse.username import UsernameToken
>>> from zeep.wsse.utils import WSU
>>> timestamp_token = WSU.Timestamp()
>>> today_datetime = datetime.datetime.today()
>>> expires_datetime = today_datetime + datetime.timedelta(minutes=10)
>>> timestamp_elements = [
... WSU.Created(today_datetime.strftime("%Y-%m-%dT%H:%M:%SZ")),
... WSU.Expires(expires_datetime.strftime("%Y-%m-%dT%H:%M:%SZ"))
...]
>>> timestamp_token.extend(timestamp_elements)
>>> user_name_token = UsernameToken('username', 'password', timestamp_token=timestamp_token)
>>> client = Client(
... 'http://www.webservicex.net/ConvertSpeed.asmx?WSDL', wsse=user_name_token
...)`
But when I try to print the various parts this is all I get:
OUTPUT
<Element {http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd}Timestamp at 0x7f46e09032c0>
2022-03-17 09:38:20.627353
2022-03-17 09:48:20.627353
[<Element {http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd}Created at 0x7f46e0903400>, <Element {http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd}Expires at 0x7f46e0916f40>]
None
<zeep.wsse.username.UsernameToken object at 0x7f46e0911fa0>
<zeep.client.Client object at 0x7f46e0911f40>
Did you manage to solve it, I have a similar problem, the code does not generate the nonce, it only generates it if it is with the use_digest=True option, the problem is that I need the password in plain text, and also the nonce with the hash
I solved the problem this way, I'm not a programmer, I just adapted the class so that it would return the xml in the format you need. this worked for me.
from zeep.wsse import utils import base64 import hashlib import os from zeep import ns
#Esta clase es original de zeep pero la modifique para que me devolviera el xml #con el formato que espera el servicio, username password tex y nonce. class UsernameToken:
username_token_profile_ns = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0" # noqa
soap_message_secutity_ns = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0" # noqa
def __init__(
self,
username,
password=None,
password_digest=None,
use_digest=False,
nonce=None,
created=None,
timestamp_token=None,
zulu_timestamp=None,
hash_password=None,
):
"""
Some SOAP services want zulu timestamps with Z in timestamps and
in password digests they may want password to be hashed before
adding it to nonce and created.
"""
self.username = username
self.password = password
self.password_digest = password_digest
self.nonce = nonce
self.created = created
self.use_digest = use_digest
self.timestamp_token = timestamp_token
self.zulu_timestamp = zulu_timestamp
self.hash_password = hash_password
def apply(self, envelope, headers):
security = utils.get_security_header(envelope)
# The token placeholder might already exists since it is specified in
# the WSDL.
token = security.find("{%s}UsernameToken" % ns.WSSE)
if token is None:
token = utils.WSSE.UsernameToken()
security.append(token)
if self.timestamp_token is not None:
security.append(self.timestamp_token)
# Create the sub elements of the UsernameToken element
elements = [utils.WSSE.Username(self.username)]
if self.password is not None or self.password_digest is not None:
if self.use_digest:
pass
else:
elements.extend(self._create_password_digest())
token.extend(elements)
return envelope, headers
def verify(self, envelope):
pass
def _create_password_digest(self):
if self.nonce:
nonce = self.nonce.encode("utf-8")
else:
nonce = os.urandom(16)
timestamp = utils.get_timestamp(self.created, self.zulu_timestamp)
if isinstance(self.password, str):
password = self.password.encode("utf-8")
else:
password = self.password
# digest = Base64 ( SHA-1 ( nonce + created + password ) )
if not self.password_digest and self.hash_password:
digest = base64.b64encode(
hashlib.sha1(
nonce + timestamp.encode("utf-8") + hashlib.sha1(password).digest()
).digest()
).decode("ascii")
elif not self.password_digest:
digest = base64.b64encode(
hashlib.sha1(nonce + timestamp.encode("utf-8") + password).digest()
).decode("ascii")
else:
digest = self.password_digest
return [
utils.WSSE.Password(
self.password, Type="%s#PasswordText" % self.username_token_profile_ns
),
utils.WSSE.Nonce(
base64.b64encode(nonce).decode("utf-8"),
EncodingType="%s#Base64Binary" % self.soap_message_secutity_ns,
),
utils.WSU.Created(timestamp),
]