WS-Security headers and timestamp

Hi Dino,

this is continuation for below post. 

https://www.googlecloudcommunity.com/gc/Apigee/Applying-WS-S-UsernameToken-and-WS-Security/td-p/6799...

I have captured payload from layer7 and apigee. I see there is a difference in headers and timestamp is missing from payload.

layer 7 payload:

<soapenv:Envelope
xmlns:ns4="/MDMServicesLibrary/SharedServices/MDM/Processes/Party/SearchPerson-Start-Input.xsd"
<soapenv:Header>
<wsse:Security soapenv:mustUnderstand="1"
<wsu:Timestamp>
<wsu:Created>2024-03-12T19:51:39.496228242Z</wsu:Created>
<wsu:Expires>2024-03-12T19:56:39.496Z</wsu:Expires>
</wsu:Timestamp>
<wsse:UsernameToken>
<wsse:Username>username</wsse:Username>
<wsu:Created>2024-03-12T19:51:39Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>

APIGEE payload:

<soapenv:Envelope
xmlns:ns4="/MDMServicesLibrary/SharedServices/MDM/Processes/Party/SearchPerson-Start-Input.xsd"
<soapenv:Header>
<wsse:Security soapenv:mustUnderstand="1">
<wsse:UsernameToken wsu:Id="UsernameToken-8a954b25-c8c1-43bc-9b75-cf1bdc5dc956">
<wsse:Username>username</wsse:Username>
<wsu:Created>2024-03-12T18:41:40Z</wsu:Created>
<wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">tTToQi+o0upZ2r/nrH8EzhD6o6U=</wsse:Nonce>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>
 
in apigee payload below namespaces are added under soapenv:Envelope where as in layer 7 they are added under soapenv:Header
xmlns:wsu
xmlns:wsse
 
and also timestamp tag is missing apigee payload.
 
Could you please build new jar and share with me.
Solved Solved
3 11 211
1 ACCEPTED SOLUTION

Splendid! ok that's progress. The output is in the right shape. Mostly!

I notice a couple differences between that and what you showed for Layer 7 output.

  1. the Password element is PasswordDigest for Apigee, PasswordText for Layer7. That would be material, if the endpoint on the other side does not suport PasswordDigest. To fix this, you will need to use

    <Property name="password-encoding">TEXT</Property>
    

    ...in place of

    <Property name="password-encoding">DIGEST</Property>
    
  2. The timestamps in the Apigee-generated payload do not include fractional seconds, whereas the payload from Layer7 includes fractional seconds. In theory, that should be JUST FINE, since the specifications say that the value should be an xsd dateTime, and fractional seconds are optional. But it is possible - unlikely, but possible - that the endpoint on the other side is insisting on fractional seconds.

View solution in original post

11 REPLIES 11

Hi Raj

Thanks for the details in the message.

in apigee payload below namespaces are added under soapenv:Envelope where as in layer 7 they are added under soapenv:Header

xmlns:wsu

xmlns:wsse

The placement of the declaration of the namespace prefixes does not affect the semantics of the XML. The "XML Infoset" is the same, as long as the namespace prefixes are declared "somewhere".

So I think you should not be concerned about the differences in the locations in which the namespace prefixes are declared.

Also, you didn't ask, but... the prefix strings themselves have no bearing on the XML Infoset, either. So the namespace that uses prefix wsu in one document, could use prefix wssecutilty in another document, and it would have no bearing on the semantics of the document. The reader of such documents should treat them the same.

and also timestamp tag is missing apigee payload.

In the post you cited, I wrote:

Edit: I've updated the callout so that it can now optionally inject a Timestamp element. Get version 20231213 of the callout or later for this feature. Check the README for how to use it.)

I think we are talking about this repo. The README says:

screenshot-20240313-091054.png

The README doesn't explicitly provide an example, but it cites the included proxy bundle for a working example. The policy looks like this: 

<JavaCallout name='Java-WSSEC-Inject-UsernameToken-with-Password-Digest-and-Timestamp'>
  <Properties>
    <Property name='source'>message.content</Property>
    <Property name='output-variable'>output</Property>
    <Property name='username'>emil@gaffanon.com</Property>
    <Property name='password'>my_secret_password!</Property>
    <Property name='password-encoding'>DIGEST</Property>
    <!-- add expiry property to get a Timestamp -->
    <Property name='expiry'>30s</Property>
  </Properties>
  <ClassName>com.google.apigee.callouts.wssecusernametoken.Inject</ClassName>
  <ResourceURL>java://apigee-wssecusernametoken-20231213.jar</ResourceURL>
</JavaCallout>

And the resulting WS-Security header looks like this: 

...
<wsse:Security soap:mustUnderstand="1">
  <wsu:Timestamp wsu:Id="TS-100">
    <wsu:Created>2024-03-13T16:11:55Z</wsu:Created>
    <wsu:Expires>2024-03-13T16:12:25Z</wsu:Expires>
  </wsu:Timestamp>
  <wsse:UsernameToken wsu:Id="UT-101">
    <wsse:Username>emil@gaffanon.com</wsse:Username>
    <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">octH+8pYOsQ351mQItRhnj3Fmi8=</wsse:Password>
    <wsu:Created>2024-03-13T16:11:55Z</wsu:Created>
    <wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">Woe1DZWUhUMy8LDgOofyvrhiEGc=</wsse:Nonce>
  </wsse:UsernameToken>
</wsse:Security>
...

 

Hi Dino,

some how timestamp is not getting added. here is the java callout i am using. not sure what is missing here.

 

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<JavaCallout name="Java-WSSEC-Inject-UsernameToken-with-Password-Digest">
<Properties>
<Property name="source">message.content</Property>
<Property name="output-variable">message.content</Property>
<Property name="username">esb</Property>
<Property name="password">password</Property>
<Property name="password-encoding">DIGEST</Property>
<Property name="expiry">300s</Property>
</Properties>
<ClassName>com.google.apigee.callouts.wssecusernametoken.Inject</ClassName>
<ResourceURL>java://apigee-wssecusernametoken-20231213.jar</ResourceURL>
</JavaCallout>
 
here is the header, i captured.
 
<soapenv:Header>
<wsse:Security soapenv:mustUnderstand="1">
<wsse:UsernameToken wsu:Id="UsernameToken-38fbc9c9-0801-475e-8347-8aa2375b4b90">
<wsse:Username>esb</wsse:Username>
<wsu:Created>2024-03-14T06:38:46Z</wsu:Created>
<wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">iRdJllEjbF4gQNqH9Qzgp3IQ=</wsse:Nonce>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>

Something is not right. I see you have

 

<wsse:UsernameToken wsu:Id="UsernameToken-38fbc9c9-0801-475e-8347-8aa2375b4b90">

 

The code I added for the version of 20231213 , produces a wsu:Id formatted like this:

 

 <wsse:UsernameToken wsu:Id="UT-101">

 

...which is what I showed above.  The 20231212 version of the callout could format the wsu:Id  like UsernameToken-38fbc9c9-0801-475e-8347-8aa2375b4b90 , but that is not possible in the 20231213 version.

So I am guessing you have an older version of the callout somehow in the class path.  Can you check that the ONLY jar in your API Proxy bundle is

apigee-wssecusernametoken-20231213.jar

Hi Dino,

this is the only jar i have apigee-wssecusernametoken-20231213.jar

below is the final policy xml from APIGEE X and i don't see any old reference.

 

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<APIProxy revision="75" name="mdmtest">
<DisplayName/>
<Description/>
<CreatedAt>1710398017616</CreatedAt>
<LastModifiedAt>1710398017616</LastModifiedAt>
<BasePaths>/mdmv1</BasePaths>
<Policies>
<Policy>Java-WSSEC-Inject-Username</Policy>
<Policy>RF-InvalidRequest</Policy>
<Policy>RF-UnknownRequest</Policy>
<Policy>AM-Response</Policy>
<Policy>Java-WSSEC-Inject-UsernameToken-with-Password-Digest</Policy>
<Policy>Java-WSSEC-Inject-UsernameToken-with-Plaintext-Password</Policy>
<Policy>AM-Inject-Proxy-Revision-Header</Policy>
<Policy>AM-Clean-Request-Headers-From-Response</Policy>
<Policy>AM-Not-Append-URI</Policy>
<Policy>AM-append-wssecurity</Policy>
<Policy>ML-CloudLogging</Policy>
</Policies>
<ProxyEndpoints>
<ProxyEndpoint>default</ProxyEndpoint>
</ProxyEndpoints>
<Resources>
<Resource>java://apigee-wssecusernametoken-20231213.jar</Resource>
</Resources>
<TargetEndpoints>
<TargetEndpoint>default</TargetEndpoint>
</TargetEndpoints>
</APIProxy>

 

Can you download the JAR and check it?  What's the last modified date on the JAR?

It should have a build date of Dec 13th or later. And the contents look like this:

$ tar tvf ~/Downloads/apigee-wssecusernametoken-20231213.jar 
-rw-r--r--  0 0      0          82 Dec 13 09:58 META-INF/MANIFEST.MF
drwxr-xr-x  0 0      0           0 Dec 13 09:58 META-INF/
drwxr-xr-x  0 0      0           0 Dec 13 09:58 com/
drwxr-xr-x  0 0      0           0 Dec 13 09:58 com/google/
drwxr-xr-x  0 0      0           0 Dec 13 09:58 com/google/apigee/
drwxr-xr-x  0 0      0           0 Dec 13 09:58 com/google/apigee/util/
drwxr-xr-x  0 0      0           0 Dec 13 09:58 com/google/apigee/xml/
drwxr-xr-x  0 0      0           0 Dec 13 09:58 com/google/apigee/callouts/
drwxr-xr-x  0 0      0           0 Dec 13 09:58 com/google/apigee/callouts/wssecusernametoken/
-rw-r--r--  0 0      0        3537 Dec 13 09:58 com/google/apigee/util/XmlUtils.class
-rw-r--r--  0 0      0         912 Dec 13 09:58 com/google/apigee/util/XmlUtils$1.class
-rw-r--r--  0 0      0        2607 Dec 13 09:58 com/google/apigee/util/TimeResolver.class
-rw-r--r--  0 0      0        3396 Dec 13 09:58 com/google/apigee/xml/Namespaces.class
-rw-r--r--  0 0      0        6351 Dec 13 09:58 com/google/apigee/callouts/wssecusernametoken/WssecUsernameTokenCalloutBase.class
-rw-r--r--  0 0      0       13252 Dec 13 09:58 com/google/apigee/callouts/wssecusernametoken/Inject.class
-rw-r--r--  0 0      0        1694 Dec 13 09:58 com/google/apigee/callouts/wssecusernametoken/Inject$PolicyConfiguration.class
-rw-r--r--  0 0      0        1580 Dec 13 09:58 com/google/apigee/callouts/wssecusernametoken/WssecUsernameTokenCalloutBase$PasswordEncoding.class

The SHA-256 checksum of the file I downloaded from github is: 

$ shasum -a 256 apigee-wssecusernametoken-20231213.jar
26f7261133eed821ad2d11a588b769be9f4c60f7d9141fe59632feea9dd60665  apigee-wssecusernametoken-20231213.jar

 Or, rather than "checking it", just re-pull the repo, re-build the jar, and use THAT in your proxy. 

You have an outdated JAR, somehow, if you are seeing "UsernameToken-<UUIDHERE>" in the wsu:Id field. You need to solve that on your side. That's not something I can solve for you. 

Hi Dino,

i cleaned up some stuff. Now i can see proper headers. except the order of namespaces. i still see backend(MDM app) is failing. We will check and update here.

 

<soapenv:Envelope
xmlns:ns4="/MDMServicesLibrary/SharedServices/MDM/Processes/Party/SearchPerson-Start-Input.xsd"
<soapenv:Header>
<wsse:Security soapenv:mustUnderstand="1">
<wsu:Timestamp wsu:Id="TS-112">
<wsu:Created>2024-03-14T20:03:47Z</wsu:Created>
<wsu:Expires>2024-03-14T20:08:47Z</wsu:Expires>
</wsu:Timestamp>
<wsse:UsernameToken wsu:Id="UT-113">
<wsse:Username>esb</wsse:Username>
<wsu:Created>2024-03-14T20:03:47Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</soapenv:Header>

Splendid! ok that's progress. The output is in the right shape. Mostly!

I notice a couple differences between that and what you showed for Layer 7 output.

  1. the Password element is PasswordDigest for Apigee, PasswordText for Layer7. That would be material, if the endpoint on the other side does not suport PasswordDigest. To fix this, you will need to use

    <Property name="password-encoding">TEXT</Property>
    

    ...in place of

    <Property name="password-encoding">DIGEST</Property>
    
  2. The timestamps in the Apigee-generated payload do not include fractional seconds, whereas the payload from Layer7 includes fractional seconds. In theory, that should be JUST FINE, since the specifications say that the value should be an xsd dateTime, and fractional seconds are optional. But it is possible - unlikely, but possible - that the endpoint on the other side is insisting on fractional seconds.

Yes. you correct. i received below error when i tried with DIGEST Service callout.

[3/14/24 15:21:03:879 CDT] 00000bae WSSUserRegist E   CWWSS7062E: Failed to check username [esbusertstldap] and password in the UserRegsitry: com.ibm.wsspi.wssecurity.core.SoapSecurityException: com.ibm.websphere.security.auth.WSLoginFailedException: javax.naming.AuthenticationException

do you recommend to use service callout with plaintext

 

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<JavaCallout continueOnError="false" enabled="true" name="Java-WSSEC-Inject-UsernameToken-with-Plaintext-Password">
<DisplayName>Java-WSSEC-Inject-UsernameToken-with-Plaintext-Password</DisplayName>
<Properties>
<Property name="source">message.content</Property>
<Property name="output-variable">message.content</Property>
<Property name="username">esbusertstldap</Property>
<Property name="password">@7rf</Property>
<Property name="expiry">300s</Property>
Property name="password-encoding">TEXT</Property>
</Properties>
<ClassName>com.google.apigee.callouts.wssecusernametoken.Inject</ClassName>
<ResourceURL>java://apigee-wssecusernametoken-20231213.jar</ResourceURL>
</JavaCallout>
or Service callout with DIGEST and keep encoding as TEXT
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<JavaCallout name="Java-WSSEC-Inject-UsernameToken-with-Password-Digest">
<Properties>
<Property name="source">message.content</Property>
<Property name="output-variable">message.content</Property>
<Property name="username">esbusertstldap</Property>
<Property name="password">7rf</Property>
<Property name="password-encoding">TEXT</Property>
<Property name="expiry">300s</Property>
</Properties>
<ClassName>com.google.apigee.callouts.wssecusernametoken.Inject</ClassName>
<ResourceURL>java://apigee-wssecusernametoken-20231213.jar</ResourceURL>
</JavaCallout>

Hi Dino,

I think it is working. i have wrong password in APIGEE policy. let me test more and update you. Thanks for the support.

 

I think you have to use the password encoding, either DIGEST or TEXT,  that the target endpoint requires.  In an enterprise, the connection will probably be over TLS, which means if you do have to send a plaintext password, it will be encrypted on the wire.   Even so, your intuition is spot- on:  you generally do not want to send passwords or other credentials in the clear, even over TLS.  The DIGEST option allows you to avoid that, but ... the target endpoint may or may not support DIGEST !

 

Hi Dino,

I just test DIGEST option and target endpoint is not supporting at this moment. I am going with encoding property as TEXT.

at this moment, i verified one of the flow and its working. I am closing this thread.

Thanks for all your support, Guidance and quick response on these issues.

Thanks,

Raj