UsernameToken: web service requires plain text password, but also requires nonce and Created

hausertreyen's picture

Hi all, I've spent numerous hours trying to find a workaround for this and am getting desperate. Using PHP, I'm trying to build a client to interface with the Department of Homeland Security's E-Verify web service. I think I'm in the same boat as this guy:

wso2.org/mailarchive/wsf-php-user/2007-August/000086.html

That is, the web service's specification states (from their specification document): "The password is the 8-14 character password associated with the CPS logon ID transmitted in plain-text. The https must be used to make the Web Service call secure."

 

When I set the passwordType to be plain text, of course, the Nonce and Created elements of the token are not created by WSF. Here is what the header looks like (scrubbing the user/pw):


  <soapenv:Header>
    <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" soapenv:mustUnderstand="1">
      <wsu:Timestamp xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
        <wsu:Created>2009-05-04T19:34:21.568Z</wsu:Created>
        <wsu:Expires>2009-05-04T19:39:21.568Z</wsu:Expires>
      </wsu:Timestamp>
      <wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
        <wsse:Username>user_here</wsse:Username>
        <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">plaintext_pw_here</wsse:Password>
      </wsse:UsernameToken>
    </wsse:Security>
  </soapenv:Header>

And here is their response:

<soap:Fault>
<faultcode xmlns:code="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">code:InvalidSecurityToken</faultcode>
<faultstring>Microsoft.Web.Services2.Security.SecurityFault: An invalid security token was provided ---&gt; System.Exception: WSE567: The incoming Username token must contain both a nonce and a creation time for the replay detection feature.
at Microsoft.Web.Services2.Security.Tokens.UsernameTokenManager.DetectReplay(UsernameToken token)
at Microsoft.Web.Services2.Security.Tokens.UsernameTokenManager.LoadTokenFromXml(XmlElement element)
at Microsoft.Web.Services2.Security.Tokens.SecurityTokenManager.LoadXmlSecurityToken(XmlElement element)
--- End of inner exception stack trace ---
at Microsoft.Web.Services2.Security.Tokens.SecurityTokenManager.LoadXmlSecurityToken(XmlElement element)
at Microsoft.Web.Services2.Security.Tokens.SecurityTokenManager.GetTokenFromXml(XmlElement element)
at Microsoft.Web.Services2.Security.Security.LoadToken(XmlElement element, SecurityConfiguration configuration, Int32&amp; tokenCount)
at Microsoft.Web.Services2.Security.Security.LoadXml(XmlElement element)
at Microsoft.Web.Services2.Security.SecurityInputFilter.ProcessMessage(SoapEnvelope envelope)
at Microsoft.Web.Services2.Pipeline.ProcessInputMessage(SoapEnvelope envelope)
at Microsoft.Web.Services2.WebServicesExtension.BeforeDeserializeServer(SoapServerMessage message)</faultstring>
</soap:Fault>
 
Of course, I understand that this is a logic issue in their understanding of how UsernameToken should work.  That said, they've said they're not going to fix it, so I have to find a way to work around it.  My problem is I have no idea how to do it.

I've tried various WSHeader machinations, but cannot get them to work.  Another alternative might be if I could find a way to load the outgoing XML into the DOM, do an XPATH to find the Password element and then do a manual substitution with the plain text password.  I'd even be willing to build the XML from scratch in SimpleXML or DOMXML if I could figure out how to successfully build and post it.  There are likely other alternatives I don't know.  What I do know is that I'm completely stuck and am open to anything that would work.

First, is there a way to force Nonce and Created elements to be created even if PlainText is specified in my WSSecurityToken passwordType?

Second, if not, how should I work around this?  I've tried using PHP built-in SOAP class, but cannot get that to work either (additional namespaces are required).

Thanks ahead of time!

hausertreyen's picture

RESOLVED

After a fair amount of effort, I figured out a few things. Please, someone who has a well-read blog (that google thinks highly of) post this solution. It took me forever to find the answers to two items: 1) how to use PHP to post successfully to a .NET web service, and 2) how to create a valid security token when nonce and created elements are required, but password is plaintext. In the end, I found my solution outside of WSF/PHP, unfortunately, because I'd have liked to have used the toolset. Also, can someone let me know if the following statement is correct? (and correctly stated) That is, .NET web services apparently require that you pass parameters as objects and not as standard variables. So instead of doing this: $a = "Bonds"; $b = "Barry"; $params = array('variable_one'=>$a, 'variable_two'=>$b); $result = $client->method_one($params); You have to do this: class method_one { public $variable_one; public $variable_two; } $params = new method_one; $params->variable_one = "Bonds"; $params->variable_two = "Barry"; $client = new SoapClient ($wsdl, array('features' => SOAP_USE_XSI_ARRAY_TYPE + SOAP_SINGLE_ELEMENT_ARRAYS, 'classmap' => array('method_one' => 'method_one')) ); $client->method_one($params); Can someone explain to me why the standard variable method call doesn't work in a bit more detail? Anyway, here's the code for the PHP header UsernameToken to work with .NET and a plain text password: function getNonce() { $time = ceil(time() / $ttl) * $ttl; return md5(date('Y-m-d H:i:s:u', $time).':'.$_SERVER['REMOTE_ADDR'].':'.'my_privatekey'); } class method_one { public $variable_one; public $variable_two; } // set up some variables $ns = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd'; $wsu = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd'; $username = "your_username"; $password = "your_plaintext_password"; $nonce = getNonce(); $wsdl = "your_wsdl_here"; // setting my time to simply be GMT $created = date ("Y-m-d\TH:i:s", mktime (date("H") +5, date("i"), date("s"), date("m"), date("d"), date("Y")))."Z"; $token = new stdClass; $token->Username = new SOAPVar($username, XSD_STRING, null, null, null, $ns); $token->Password = new SOAPVar($password, XSD_STRING, null, null, null, $ns); $token->Nonce = new SOAPVar($nonce, XSD_STRING, null, null, null, $ns); $token->Created = new SOAPVar($created, XSD_STRING, null, null, null, $wsu); $wsec = new stdClass; $wsec->UsernameToken = new SoapVar($token, SOAP_ENC_OBJECT, null, null, null, $ns); $headers = new SOAPHeader($ns, 'Security', $wsec, true); $params = new method_one; $params->variable_one = "Bonds"; $params->variable_two = "Barry"; $client = new SoapClient ($wsdl, array("trace" => 1,"exceptions" => 0, 'features' => SOAP_USE_XSI_ARRAY_TYPE + SOAP_SINGLE_ELEMENT_ARRAYS, 'classmap' => array('method_one' => 'method_one')) ); $client->__setSOAPHeaders($headers); $client->method_one($params); Thanks!
thornleyk.cpit.ac.nz's picture

Correct Operation??

Not too sure about the WS-Security Specification but it looks to me that Microsoft, IBM and Orcale all seem to be requiring Nonce and Created for PlainText Assertations. The dropping of these two tags from PlainText messages also caused me no end of troubles. I made the following changes to /wsf/rampart/src/util/rampart_username_token.c Move these two lines From line 145(ish) up to round about line 66 so the become accessible to the entire rampart_username_token_build function axis2_char_t *nonce_val = NULL; axis2_char_t *created_val = NULL; Add these lines to the tail of the function so they get included when the password is plainText nonce_val = oxs_util_generate_nonce(env, 24) ; created_val = rampart_generate_time(env, 0); axiom_namespace_increment_ref(sec_ns_obj, env); nonce_ele = axiom_element_create(env, ut_node, RAMPART_SECURITY_USERNAMETOKEN_NONCE, sec_ns_obj, &nonce_node); if (nonce_ele) { axiom_element_set_text(nonce_ele, env, nonce_val , nonce_node); } created_ele = axiom_element_create(env, ut_node, RAMPART_SECURITY_USERNAMETOKEN_CREATED, wsu_ns_obj, &created_node); if (created_ele) { axiom_element_set_text(created_ele, env, created_val, created_node); } if(nonce_val){ AXIS2_FREE(env->allocator, nonce_val); nonce_val = NULL; } if(created_val) { AXIS2_FREE(env->allocator, created_val); created_val = NULL; } Thats it now when recompiled you get the tags on plain text too
library project main code
Learn Cloud
Learn
Cloud

The WSO2 Application Server is a reliable application server that can host your enterprise web applications. The WSO2 Application Server as a Service is offered in StratosLive, the WSO2 Platform as a Service. This article explains how a simple web application can be developed and deployed from Carbon Studio to the WSO2 Application Server...

Latest Webinar
Different groups within an organization need to monitor different Key Performance Indicators (KPIs) - An operations team will be interested in the response times of business services and loads of each service,..
Thursday, February 9th 2012, 09.00 AM (PST)

Thursday, February 9th 2012, 10.00 AM (GMT)