Usuário com melhor resposta
Assinar Mensagens SOAP Interceptadas do WebService - TraceExtension/SoapExtension

Pergunta
-
Olá a todos.
Estou enfrentando um problema e gostaria de saber se alguém pode me dar mais informações sobre a solução.
A questão é que minha aplicação (VB.net, desktop) deve consumir um WebService, mas para fazer isso o xml de envio precisa estar assinado com certificado digital. Contudo, o parâmetro do método que estou chamando não é um XML, é um objeto e não vai contemplar a assinatura, ele contém apenas os atributos que devem ser preenchidos.
A assinatura deve ser feita através de uma interceptação da mensagem SOAP enviada ao método deste webservice, e é ai que os problemas iniciam.
Eu consegui alguma informação sobre TraceExtension/SoapExtension (http://msdn.microsoft.com/en-us/library/system.web.services.protocols.soapextension(VS.80).aspx) mas confesso que ficou bastante nebuloso a forma como devo aplicar a classe de extensão a minha aplicação.
Agradeço se alguém puder me dar uma explicação mais elucidativa sobre o problema.
Respostas
-
Muito bem. Caso possa interessar a alguém, vou deixar aqui como resposta a forma como foi resolvido o problema descrito na pergunta.
Começa pelo fato de que como a ideia era de apenas assinar a mensagem SOAP não foi preciso implementar todos os métodos da classe SoapExtension.
Ficou assim:
Imports System Imports System.Collections.Generic Imports System.Text Imports System.Web.Services.Protocols Imports System.IO Imports System.Xml Imports System.Runtime.InteropServices Public Class TraceExtension Inherits SoapExtension Private oldStream As Stream Private newStream As Stream Private Shared RequestXml As XmlDocument Private Shared ResponseXml As XmlDocument <ComVisible(True)> _ Public Shared ReadOnly Property XmlRequest() As XmlDocument Get Return RequestXml End Get End Property Public Shared ReadOnly Property XmlResponse() As XmlDocument Get Return ResponseXml End Get End Property Public Overrides Function ChainStream(ByVal stream As Stream) As Stream oldStream = stream newStream = New MemoryStream() Return newStream End Function Public Function getXml() As String Return XmlRequest.InnerXml End Function Public Overrides Sub ProcessMessage(ByVal message As SoapMessage) Select Case message.Stage Case SoapMessageStage.BeforeSerialize Case SoapMessageStage.AfterSerialize ''* ''* Assinatura do XML ''* ''Obtem o xml do objeto já com o envelope SOAP RequestXml = GetSoapEnvelope(newStream) 'Realiza Assinatura (-->AQUI CHAMA A CLASSE QUE IMPLEMENTA A ASSINATURA COM CERTIFICADO X509Certificate2) RequestXml = XmlSigner.SignXml(RequestXml, "MINHA ID ASSINADA", "MINHA TAG ASSINADA") ''Passa o xml assinado Dim memoryStream As MemoryStream = New MemoryStream() Dim xmlWriter As XmlWriter = xmlWriter.Create(memoryStream) RequestXml.WriteTo(xmlWriter) xmlWriter.Flush() ''Volta o memoryStream para o início memoryStream.Position = 0 CopyStream(memoryStream, oldStream) memoryStream.Close() Exit Select Case SoapMessageStage.BeforeDeserialize CopyStream(oldStream, newStream) ResponseXml = GetSoapEnvelope(newStream) Exit Select Case SoapMessageStage.AfterDeserialize Exit Select End Select End Sub Private Function GetSoapEnvelope(ByVal stream As Stream) As XmlDocument Dim xml As XmlDocument = New XmlDocument() stream.Position = 0 Dim reader As StreamReader = New StreamReader(stream) xml.LoadXml(reader.ReadToEnd()) stream.Position = 0 Return xml End Function Private Sub CopyStream(ByVal from As Stream, ByVal tto As Stream) Dim reader As StreamReader = New StreamReader(from) Dim writer As StreamWriter = New StreamWriter(tto) writer.WriteLine(reader.ReadToEnd()) writer.Flush() End Sub #Region " NOP" Public Overrides Function GetInitializer(ByVal methodInfo As LogicalMethodInfo, ByVal attribute As SoapExtensionAttribute) As Object Return Nothing End Function Public Overrides Function GetInitializer(ByVal WebServiceType As Type) Return Nothing End Function Public Overrides Sub Initialize(ByVal initializer As Object) End Sub #End Region End Class
Para que a classe de interceptação (TraceExtension) funcionasse corretamente, a solução que encontramos foi registra-la antes de executar a chamada para o método do WebService.
Resumindo bastante a chamada do método de registro ficou assim:
Private Sub btnExecutar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnExecutar.Click Try 'REGISTRO DA CLASSE DE INTERCEPTACAO RegisterSoapExtension(GetType(TraceExtension).BaseType, 1, PriorityGroup.Low) ... RespostaXML = MeuWS.MeuMetodo(EnvioXML) ... Catch ex As Exception Throw New Exception(ex.Message) End Try End Sub
Também um pouco resumido, o método de registro ficou assim:
<ComVisible(True)> _ <ReflectionPermission(SecurityAction.Demand)> _ Public Sub RegisterSoapExtension(ByVal type As Type, ByVal priority As Integer, ByVal group As PriorityGroup) Dim wss As WebServicesSection = WebServicesSection.Current Dim readOnlyField As FieldInfo = GetType(System.Configuration.ConfigurationElementCollection).GetField("bReadOnly", BindingFlags.NonPublic OrElse BindingFlags.Instance) readOnlyField.SetValue(wss.SoapExtensionTypes, False) Dim oElement As SoapExtensionTypeElement = New SoapExtensionTypeElement() oElement.Type = GetType(TraceExtension) oElement.Priority = priority oElement.Group = group wss.SoapExtensionTypes.Add(oElement) Dim resetModifiedMethod As MethodInfo = GetType(System.Configuration.ConfigurationElement).GetMethod("ResetModified", BindingFlags.NonPublic OrElse BindingFlags.Instance) resetModifiedMethod.Invoke(wss.SoapExtensionTypes, Nothing) Dim setReadOnlyMethod As MethodInfo = GetType(System.Configuration.ConfigurationElement).GetMethod("SetReadOnly", BindingFlags.NonPublic OrElse BindingFlags.Instance) setReadOnlyMethod.Invoke(wss.SoapExtensionTypes, Nothing) End Sub
E foi assim que funcionou.
- Marcado como Resposta Feco_Rocha sexta-feira, 23 de maio de 2014 19:23