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.
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.
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.