Modificando una respuesta SOAP en WCF

Si tienes un escenario de interop con webservices Java, puede que necesitas modificar el mensaje SOAP que envías o la respuesta que recibes desde el servidor.

En nuestro caso, para solucionar un problema de verificación de certificados con WS-Security, necesitábamos modificar la respuesta SOAP, para añadir un certificado X509 a la respuesta, y poder validar la firma del mensaje.

Para esto, WCF nos provee la clase TextMessageEncoder. Hay un ejemplo muy bueno en el paquete de ejemplos de WCF , que es perfecto para nuestro propósito. En la ruta WCF\Extensibility\MessageEncoder\Text\CS\library están las clases que necesitamos.

Para utilizarlas, es necesario copiar a nuestra solución:

  • CustomTextMessageEncoder.cs
  • CustomTextMessageEncoderFactory.cs
  • CustomTextMessageEncodingBindingElement.cs

Luego, en la clase CustomTextMessageEncoder está el método ReadMessage. Acá es donde se transforma lo que se lee desde la red (la respuesta) en un objeto de la clase Message, que es finalmente procesado por WCF y deserializado como respuesta del consumo del servicio, por lo que si queremos modificar el mensaje éste es el lugar apropiado.

En nuestro caso, queremos obtener el valor del atributo Id del elemento SecurityTokenReference, y generar un BinarySecurityToken para que WCF pueda validar la firma del mensaje.


public override Message ReadMessage(ArraySegment<byte> buffer, BufferManager bufferManager, string contentType)
{
byte[] msgContents = new byte[buffer.Count];
Array.Copy(buffer.Array, buffer.Offset, msgContents, 0, msgContents.Length);
bufferManager.ReturnBuffer(buffer.Array);

MemoryStream stream;

var sxml = UTF8Encoding.UTF8.GetString(msgContents);
var els = XElement.Parse(sxml);

XNamespace wsse = “http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd”;
XNamespace wsu = “http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd”;
var s = (from el in els.Descendants(wsse + “SecurityTokenReference”)
select el).FirstOrDefault();
if (s != null)
{
var elid = s.Attribute(wsu + “Id”);
string elref = elid.Value;

var xref = new XElement(wsse + “Reference”);
xref.Add(new XAttribute(“URI”, “#” + elref));
s.RemoveNodes();
s.Add(xref);

var xbin = new XElement(wsse + “BinarySecurityToken”);
xbin.Add(new XAttribute(wsu + “Id”, elref));
xbin.Add(new XAttribute(“ValueType”, “http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3”));
xbin.Add(new XAttribute(“EncodingType”, “http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary”));
xbin.Value = Constantes.CERT_TRANSBANK;

var sec = (from elss in els.Descendants(wsse + “Security”) select elss).FirstOrDefault();
if (sec != null)
sec.AddFirst(xbin);

var xstr = els.ToString(SaveOptions.DisableFormatting);
stream = new MemoryStream(UTF8Encoding.UTF8.GetBytes(xstr));
}
else
stream = new MemoryStream(msgContents);
return ReadMessage(stream, int.MaxValue);
}

Finalmente, declaramos esta clase como una extensión en el app.config o web.config (en este caso el namespace es ConsoleApplication1):


<extensions>
<bindingElementExtensions>
<add name=”customTextMessageEncoding” type=”ConsoleApplication1.CustomTextMessageEncodingElement, ConsoleApplication1″/>
</bindingElementExtensions>
</extensions>

Y en la declaración del binding declaramos el uso de esta extensión:


<customBinding>
<binding name=”NewBinding0″>
<customTextMessageEncoding messageVersion=”Soap11″ encoding=”UTF-8″ />
<security allowSerializedSigningTokenOnReply=”true” authenticationMode=”MutualCertificate”
messageProtectionOrder=”SignBeforeEncrypt” messageSecurityVersion=”WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10″>
<localClientSettings detectReplays=”false” />
</security>
<httpTransport />
</binding>
</customBinding>

Agregar un comentario