I'm writing a WCF
Client that consumes a non-.Net web service, using WS-Security. The service's response contains a Security header with mustUnderstand set to true.
Using a ServiceModelListener, I do see actual data coming back from the service. The WCF client fails, however, because it is not processing the Security header.
<env:Header>
<wsse:Security env:mustUnderstand="1" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsu:Timestamp wsu:Id="timestamp">
<wsu:Created>2012-03-28T13:43:54.474Z</wsu:Created>
<wsu:Expires>2012-03-28T13:48:54.474Z</wsu:Expires>
</wsu:Timestamp>
</wsse:Security>
</env:Header>
WCF Client Error Message:
The header 'Security' from the namespace 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd' was not understood by the recipient of this message, causing the message to not be processed. This error typically indicates that the sender of this message has enabled a communication protocol that the receiver cannot process. Please ensure that the configuration of the client's binding is consistent with the service's binding.
My WCF
client doesn't need any of the timestamp info. Is there an easy way to stub in a processing routine? I've already tried extending the Response class & adding a [MessageHeader] property.
EDIT:
Asked another way: How do I implement a
WCF
client that accepts custom header elements that are marked Must Understand?
I ran into a similar issue. I am not sure if this is useful or not.
MSDN WCF Extensibility
http://blogs.msdn.com/b/carlosfigueira/archive/2011/04/19/wcf-extensibility-message-inspectors.aspx
The setup here is Certificate based, Oracle Application Server 10g, and .Net to consume the services. Using SOAPUi was very useful while trying to figure out what was happening with the Request and then the response.
I have not tried modifying the code to use basicHttpBinding, but I used WSHttpBinding as the base of my configuration in code. Then used
WSHttpBinding binding = new WSHttpBinding()
{
CloseTimeout = new TimeSpan(0, 1, 0),
OpenTimeout = new TimeSpan(0, 1, 0),
SendTimeout = new TimeSpan(0, 1, 0),
AllowCookies = false,
BypassProxyOnLocal = false,
HostNameComparisonMode = HostNameComparisonMode.StrongWildcard,
MaxBufferPoolSize = 524288,
MaxReceivedMessageSize = 65536,
MessageEncoding = WSMessageEncoding.Text,
UseDefaultWebProxy = false,
ReaderQuotas = new System.Xml.XmlDictionaryReaderQuotas()
{
MaxDepth = 32,
MaxArrayLength = 16384,
MaxBytesPerRead = 4096,
MaxNameTableCharCount = 16384,
MaxStringContentLength = 8192
}
};
binding.Security.Mode = SecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
binding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.None;
binding.Security.Transport.Realm = string.Empty;
binding.Security.Message.ClientCredentialType = MessageCredentialType.Certificate;
binding.Security.Message.EstablishSecurityContext = true;
binding.Security.Message.NegotiateServiceCredential = true;
CustomBinding customBinding = new CustomBinding();
BindingElementCollection collection = binding.CreateBindingElements();
Looped through for the TextMessageEncodingBindingElement to set Soap11 and AddressingVersion to None.
foreach (BindingElement element in collection)
{
if (typeof(TextMessageEncodingBindingElement) == element.GetType())
{
TextMessageEncodingBindingElement item = element as TextMessageEncodingBindingElement;
if (null != item)
{
item.MessageVersion = MessageVersion.CreateVersion(EnvelopeVersion.Soap11, AddressingVersion.None);
customBinding.Elements.Add(item);
}
}
else
customBinding.Elements.Add(element);
}
I used the ChannelFactory and added an EndPoint Behavior for a Message Inspector. At this point I then had control of the request and I could add the appropriate header and modified the mustUnderstand on the Action.
Using SOAPUi I took my Message.ToString() and put that in SOAPUI and tested the request. Once the items that were needed were added to the request, it was then determined that the OAS server was not replying with all the necessary elements. Using the message inspector for the reply I modified the message to include the missing headers. I can't remember where I found the base code for the message inspector, but you would need to modify your code to utlize it properly.
For my example here are some snippets.
For the transform message in
public object BeforeSendRequest
I needed to modify the Header, so using a for loop I grabbed the XElement and added the OASIS header and added a To header.
XNamespace xmlns = "http://schemas.xmlsoap.org/soap/envelope/";
XElement securityHeader = new XElement(
xmlns + "Security",
new XAttribute(xmlns + "wsse", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"),
new XAttribute(xmlns + "xmlns", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"),
new XAttribute(xmlns + "mustUnderstand", "0"));
element.Add(securityHeader);
I also had to modify the Action Header
else if (localName.Equals("Action", StringComparison.InvariantCultureIgnoreCase))
{
foreach (XAttribute a in element.Attributes())
{
if (a.Name.LocalName == "mustUnderstand")
a.Value = "0";
}
}
My problem was that the Service didn't reply with an Action Header
So in the
public void AfterReceiveReply
I called my TransformReply returning type Message with something like the following. You may need to modify values for the string.Empty, but this is just an example.
...
Message reply = Message.CreateMessage(message.Version, null, reader);
reply.Headers.Add(MessageHeader.CreateHeader("Action", string.Empty, string.Empty, false));
reply.Properties.CopyProperties(message.Properties);
...
I would really suggest using a tool such as SOUPUI to beable to mess with the envelope and see the reply. If you do SSL, you'll need to create a cacert file and place it in the SSLSettings of the preferences.