Search code examples
asp.netweb-serviceswcfsoapwsdl

How to specify that the Timestamp that's part of WS-Security needs to be signed + fix error that client did not sign timestamp with id xxx?


I have implemented a WSDL of an external service. They have the endpoint of this implementation and we need to configure our WCF configuration so that our WSDL ends up being like theirs so that that company is able to call our service and our WCF service knows what to do.

Their WSDL contains these signed elements:

<wsp:Policy wsu:Id="SecureMessagePolicy">
    <wsp:ExactlyOne>
        <wsp:All>
            <sp:SignedParts>
                <sp:Body/>
                <sp:Header Name="To" Namespace="http://www.w3.org/2005/08/addressing"/>
                <sp:Header Name="From" Namespace="http://www.w3.org/2005/08/addressing"/>
                <sp:Header Name="FaultTo" Namespace="http://www.w3.org/2005/08/addressing"/>
                <sp:Header Name="ReplyTo" Namespace="http://www.w3.org/2005/08/addressing"/>
                <sp:Header Name="MessageID" Namespace="http://www.w3.org/2005/08/addressing"/>
                <sp:Header Name="RelatesTo" Namespace="http://www.w3.org/2005/08/addressing"/>
                <sp:Header Name="Action" Namespace="http://www.w3.org/2005/08/addressing"/>
            </sp:SignedParts>
            <sp:SignedElements>
                <sp:XPath>/*[namespace-uri()='http://schemas.xmlsoap.org/soap/envelope/' and local-name()='Envelope']/*[namespace-uri()='http://schemas.xmlsoap.org/soap/envelope/' and local-name()='Header']/*[namespace-uri()='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd' and local-name()='Security']/*[namespace-uri()='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd' and local-name()='Timestamp']</sp:XPath>
                <sp:XPath>/*[namespace-uri()='http://www.w3.org/2003/05/soap-envelope' and local-name()='Envelope']/*[namespace-uri()='http://www.w3.org/2003/05/soap-envelope' and local-name()='Header']/*[namespace-uri()='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd' and local-name()='Security']/*[namespace-uri()='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd' and local-name()='Timestamp']</sp:XPath>
            </sp:SignedElements>
        </wsp:All>
    </wsp:ExactlyOne>
</wsp:Policy>

The signing part of my WSDL looks like this:

<wsp:Policy wsu:Id="{{POLICE_NAME_REQUEST}}">
    <wsp:ExactlyOne>
        <wsp:All>
            <sp:SignedParts xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
                <sp:Body/>
                <sp:Header Name="To" Namespace="http://www.w3.org/2005/08/addressing"/>
                <sp:Header Name="From" Namespace="http://www.w3.org/2005/08/addressing"/>
                <sp:Header Name="FaultTo" Namespace="http://www.w3.org/2005/08/addressing"/>
                <sp:Header Name="ReplyTo" Namespace="http://www.w3.org/2005/08/addressing"/>
                <sp:Header Name="MessageID" Namespace="http://www.w3.org/2005/08/addressing"/>
                <sp:Header Name="RelatesTo" Namespace="http://www.w3.org/2005/08/addressing"/>
                <sp:Header Name="Action" Namespace="http://www.w3.org/2005/08/addressing"/>
            </sp:SignedParts>
        </wsp:All>
    </wsp:ExactlyOne>
</wsp:Policy>
<wsp:Policy wsu:Id="{{POLICE_NAME_RESPONSE}}">
    <wsp:ExactlyOne>
        <wsp:All>
            <sp:SignedParts xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
                <sp:Body/>
                <sp:Header Name="To" Namespace="http://www.w3.org/2005/08/addressing"/>
                <sp:Header Name="From" Namespace="http://www.w3.org/2005/08/addressing"/>
                <sp:Header Name="FaultTo" Namespace="http://www.w3.org/2005/08/addressing"/>
                <sp:Header Name="ReplyTo" Namespace="http://www.w3.org/2005/08/addressing"/>
                <sp:Header Name="MessageID" Namespace="http://www.w3.org/2005/08/addressing"/>
                <sp:Header Name="RelatesTo" Namespace="http://www.w3.org/2005/08/addressing"/>
                <sp:Header Name="Action" Namespace="http://www.w3.org/2005/08/addressing"/>
            </sp:SignedParts>
        </wsp:All>
    </wsp:ExactlyOne>
</wsp:Policy>
<wsp:Policy wsu:Id="{{POLICE_NAME_FAULT}}">
    <wsp:ExactlyOne>
        <wsp:All>
            <sp:SignedParts xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
                <sp:Body/>
                <sp:Header Name="To" Namespace="http://www.w3.org/2005/08/addressing"/>
                <sp:Header Name="From" Namespace="http://www.w3.org/2005/08/addressing"/>
                <sp:Header Name="FaultTo" Namespace="http://www.w3.org/2005/08/addressing"/>
                <sp:Header Name="ReplyTo" Namespace="http://www.w3.org/2005/08/addressing"/>
                <sp:Header Name="MessageID" Namespace="http://www.w3.org/2005/08/addressing"/>
                <sp:Header Name="RelatesTo" Namespace="http://www.w3.org/2005/08/addressing"/>
                <sp:Header Name="Action" Namespace="http://www.w3.org/2005/08/addressing"/>
            </sp:SignedParts>
        </wsp:All>
    </wsp:ExactlyOne>
</wsp:Policy>

WCF generates these things for the request, response and fault. Their WSDL has it done for these 3 things just once. I don't think this really matters.

As you can see, our WSDL says that the same thing should be signed. (Body, To, From, FaultTo, ReplyTO, MessageID, RelatesTo, Action). EXCEPT SignedElements part which specifies that the TimeStamp should also be signed.

The Timestamp is part of the http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd namespace (called wsu in their WSDL):

<wsu:Timestamp ... >
    <wsu:Created>2011-11-30T11:12:12.459Z</wsu:Created>
    <wsu:Expires>2011-12-01T11:12:12.459Z</wsu:Expires>
</wsu:Timestamp>

My binding now looks like this:

<binding>
      <MissingWSAddressingHeadersTextEncoding />
      <security
        authenticationMode="MutualCertificate"
        messageSecurityVersion="WSSecurity10WSTrust13WSSecureConversation13WSSecurityPolicy12BasicSecurityProfile10"
        enableUnsecuredResponse="false"
        messageProtectionOrder="EncryptBeforeSign"
        includeTimestamp="true"
        defaultAlgorithmSuite="TripleDesRsa15"
        allowSerializedSigningTokenOnReply="false"/>
      <httpsTransport requireClientCertificate="true"/>
</binding>

MissingWSAddressingHeadersTextEncoding is a custom encoder that uses a TextMessageEncoder underwater.

The question

How do I specify that my WSDL should also sign the Timestamp? I guess that if I return a response with an unsigned response, errors might occur.


Solution

  • I have found the answer.

    Apparently, a timestamp is ALWAYS signed when signing is used (docs.microsoft), so I do not need to worry about signing the timestamp.

    When I checked SOAP UI's response, I just got FailedAuthentication errors. I implemented [Recommended WCF logging] which told me that the timestamp was not signed by SOAP UI, which is weird because I did specify this in the WS outgoing config.

    My fix was to use MutualCertificateDuplex. This did sign the Timestamp. I also put allowSerializedSigningTokenOnReply on true, although I don't think this matters.