Correct Way Communicate Wsse Usernametoken for Soap Webservice

Correct way communicate WSSE Usernametoken for SOAP webservice

If you need to send UserName over HTTPS you can use standard approach (if your WSDL is correctly defined this should be created for you automatically by adding service reference):

<bindings>
<basicHttpBinding>
<binding name="secured">
<security mode="TransportWithMessageCredential">
<message clientCredentialType="UserName" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint name="..." address="https://..." contract="..." binding="basicHttpBinding"
bindingConfiguration="secured" />
</client>

Ar you can define binding in code:

var basicHttpBinding = new BasicHttpBinding(BasicHttpSecurityMode.TransportWithMessageCredential);
basicHttpBinding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;

You will set credentials in proxy as you do it now:

client.ClientCredentials.UserName.UserName = "bob";
client.ClientCredentials.UserName.Password = "1234";

If you only need UserNameToken profile over HTTP without any other WS-Security infrastructure the simplest approach is using ClearUserNameBinding.

If you need same user name and password for all requests from the client you can use simple basicHttpBinding without any security and include the static header from configuration:

<client>
<endpoint ...>
<headers>
<wsse:UsernameToken xmlns:wsse='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd' >
<wsse:Username>Bob</wsse:Username>
<wsse:Password Type='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText'>
1234
</wsse:Password>
</wsse:UsernameToken>
</headers>
</endpoint>
</client>

If you need anything more complex show the relevant part of WSDL (security assertion) or sample SOAP request. Also mention if you are required to use HTTP or HTTPS.

WS-Security: What is the correct way to include multiple user identities in the SOAP Security header?

The most common case of using several users I might recollect is passing message through several intermediate nodes. In terms of SOAP specification they are named Actors and requires separate wsse:Security element with different soap:actor attributes. It corresponds to option 2) from your question.

Consider the next example from here:

<soap:Header>        
<wsse:Security xmlns:wsse="http://schemas.xmlsoap.org/ws/2003/06/secext">
<wsse:UsernameToken wsu:Id="sample" xmlns:wsu="http://schemas.xmlsoap.org/ws/2003/06/utility">
<wsse:Username>sample</wsse:Username>
<wsse:Password Type="wsse:PasswordText">oracle</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
<wsse:Security soap:actor="oracle" xmlns:wsse="http://schemas.xmlsoap.org/ws/2003/06/secext">
<wsse:UsernameToken wsu:Id="oracle" xmlns:wsu="http://schemas.xmlsoap.org/ws/2003/06/utility">
<wsse:Username>oracle</wsse:Username>
<wsse:Password Type="wsse:PasswordText">oracle</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</soap:Header>




This example contains two blocks. The one with "oracle" attribute is used to authenticate the end-user, another without this attribute is used for authentication on the front-end gateway.

You may consider option 1) from your question if your use case supposes simultaneous authentication of several users that are not SOAP actors.

UsernameToken with Timestamp token || Python || WS-Security (WSSE)

I'm not sure what issue you are facing, but that code should be sufficient to get something working. Maybe the output is not what you expect, so I'll expand a bit on that.

Assuming this is the WSDL of that service (since the webservice itself isn't working), in order to call it and also add the security header, you could do this:

import datetime
from zeep import Client
from zeep.wsse.username import UsernameToken
from zeep.wsse.utils import WSU
from zeep.plugins import HistoryPlugin
from lxml import etree

def print_history(h):
print(etree.tostring(h.last_sent["envelope"], encoding = "unicode", pretty_print = True))
print(etree.tostring(h.last_received["envelope"], encoding = "unicode", pretty_print = True))

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)

history = HistoryPlugin()
client = Client(
'http://www.webservicex.net/ConvertSpeed.asmx?WSDL',
wsse = user_name_token,
plugins = [history]
)

response = client.service.ConvertSpeed(100.00, 'kilometersPerhour', 'milesPerhour')

print_history(history)

The call to the service will produce the following SOAP message:

<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
<soap-env:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:UsernameToken>
<wsse:Username>username</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">password</wsse:Password>
</wsse:UsernameToken>
<wsu:Timestamp xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsu:Created>2022-03-20T14:33:15Z</wsu:Created>
<wsu:Expires>2022-03-20T14:43:15Z</wsu:Expires>
</wsu:Timestamp>
</wsse:Security>
</soap-env:Header>
<soap-env:Body>
<ns0:ConvertSpeed xmlns:ns0="http://www.webserviceX.NET/">
<ns0:speed>100.0</ns0:speed>
<ns0:FromUnit>kilometersPerhour</ns0:FromUnit>
<ns0:ToUnit>milesPerhour</ns0:ToUnit>
</ns0:ConvertSpeed>
</soap-env:Body>
</soap-env:Envelope>

If you want to use zeep, I suggest you use it with the real service you are trying to call, not with some sample from the Internet that isn't available. I guess in the zeep documentation they needed some example service to call, but I'm not even sure that service wanted an authentication header.

Calling a soap webservice from java with nonce in security headers

The Oasis reference for the UsernameToken helped me fill in some of the blanks. Pages 7,8,9 are most appropriate in this case. In particular these sections

/wsse:UsernameToken/wsse:Nonce

This optional element specifies a cryptographically random nonce. Each message
including a element MUST use a new nonce value in order for web
service producers to detect replay attacks.

and

/wsse:UsernameToken/wsse:Nonce/@EncodingType

This optional attribute URI specifies the encoding type of the nonce (see the definition of
<wsse:BinarySecurityToken> for valid values). If this attribute isn't specified then
the default of Base64 encoding is used.

In regards to generating the 'cryptographically random' nonce, can suggest you use this answer and then create an encoded string from it. Base64 encoding in your case, as that is the encodingType you are using in your XML request above.

How to read Soap WSSE Headers from Symfony 2.0

I finally admitted that samples provided by Symfony implies that WSSE headers are brought by HTTP layer, not by the SOAP envelope. So I made a workaround to read SOAP headers :

The WsseListener handle method :

   public function handle(GetResponseEvent $event)
{
$request = $event->getRequest();

$decodedHeaders = WsseHeadersDecoder::getHeaders($request);

if (!$decodedHeaders || !is_array($decodedHeaders))
{
return;
}

$token = new WsseUserToken();
$token->setUser($decodedHeaders['username']);

$token->digest = $decodedHeaders['passwordDigest'];
$token->nonce = $decodedHeaders['nonce'];
$token->created = $decodedHeaders['created'];

// ...
}

WsseHeadersDecoder::getHeaders method :

   static public function getHeaders(Request $request)
{
//HTTP headers (as described here : http://symfony.com/doc/2.0/cookbook/security/custom_authentication_provider.html#the-listener
if ($request->headers->has('x-wsse'))
{
$wsseRegex = '/UsernameToken Username="([^"]+)", PasswordDigest="([^"]+)", Nonce="([^"]+)", Created="([^"]+)"/';
if (1 !== preg_match($wsseRegex, $request->headers->get('x-wsse'), $matches))
{
return false;
}
else
{
$username = $matches[1];
$passwordDigest = $matches[2];
$nonce = $matches[3];
$created = $matches[4];
}
}
//Classic SOAP headers
else
{
//PéCé: Clear XML namespace prefixes to handle with SimpleXML
$decodedRequest = preg_replace("/(<\/?)([-\w]+):([^>]*>)/", "$1$3", $request->getContent());
$xmlRequest = simplexml_load_string($decodedRequest);

if (
!isset($xmlRequest->Header->Security->UsernameToken->Username)
|| !isset($xmlRequest->Header->Security->UsernameToken->Password)
|| !isset($xmlRequest->Header->Security->UsernameToken->Nonce)
|| !isset($xmlRequest->Header->Security->UsernameToken->Created)
)
{
return false;
}
else
{
$username = (string) $xmlRequest->Header->Security->UsernameToken->Username;
$passwordDigest = (string) $xmlRequest->Header->Security->UsernameToken->Password;
$nonce = (string) $xmlRequest->Header->Security->UsernameToken->Nonce;
$created = (string) $xmlRequest->Header->Security->UsernameToken->Created;
}
}

return array (
'username' => $username, 'passwordDigest' => $passwordDigest, 'nonce' => $nonce, 'created' => $created
);
}

Issue with Lumesse Web Services and WSSE plain text security

I too am working with the same api and had the same issue. If you scroll down to the bottom of this article: http://www.hanselman.com/blog/BreakingAllTheRulesWithWCF.aspx Scott Hanselman removes the time-stamp through code rather than through the config.



Related Topics



Leave a reply



Submit