none
Assinar Mensagens SOAP Interceptadas do WebService - TraceExtension/SoapExtension RRS feed

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

    segunda-feira, 19 de maio de 2014 19:16

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
    sexta-feira, 23 de maio de 2014 19:18