自定义MessageEncoder在不修改结构的情况下向消息添加属性

时间:2017-03-24 14:37:19

标签: c# .net wcf wcf-security

在我的previous question我尝试连接到需要自定义安全性的JAVA服务。

我已创建自定义MessageEncoder以添加服务所需的属性,但在尝试发送请求后,我收到此回复:

<?xml version='1.0' encoding='utf-8'?>
<soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope">
    <soapenv:Header xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">
        <wsa:To>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:To>
        <wsa:MessageID>urn:uuid:ab595a3b-cb2f-450c-a65a-c260c40f9802</wsa:MessageID>
        <wsa:Action>urn:queryQueryDzException</wsa:Action>
        <wsa:RelatesTo>urn:uuid:599290c3-1034-46e7-90a6-a3172e855c6d</wsa:RelatesTo>
    </soapenv:Header>
    <soapenv:Body>
        <soapenv:Fault>
            <soapenv:Code>
                <soapenv:Value>soapenv:Receiver</soapenv:Value>
            </soapenv:Code>
            <soapenv:Reason>
                <soapenv:Text xml:lang="en-US">WSDoAllReceiver: security processing failed</soapenv:Text>
            </soapenv:Reason>
            <soapenv:Detail>
                <Exception>org.apache.axis2.AxisFault: WSDoAllReceiver: security processing failed
    at org.apache.rampart.handler.WSDoAllReceiver.processBasic(WSDoAllReceiver.java:216)
    at org.apache.rampart.handler.WSDoAllReceiver.processMessage(WSDoAllReceiver.java:85)
    at org.apache.rampart.handler.WSDoAllHandler.invoke(WSDoAllHandler.java:72)
    at org.apache.axis2.engine.Phase.invokeHandler(Phase.java:340)
    at org.apache.axis2.engine.Phase.invoke(Phase.java:313)
    at org.apache.axis2.engine.AxisEngine.invoke(AxisEngine.java:262)
    at org.apache.axis2.engine.AxisEngine.receive(AxisEngine.java:168)
    at org.apache.axis2.transport.http.HTTPTransportUtils.processHTTPPostRequest(HTTPTransportUtils.java:172)
    at org.apache.axis2.transport.http.AxisServlet.doPost(AxisServlet.java:146)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:648)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:292)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:212)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:141)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
    at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:616)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
    at org.apache.catalina.ha.tcp.ReplicationValve.invoke(ReplicationValve.java:318)
    at org.apache.catalina.ha.session.JvmRouteBinderValve.invoke(JvmRouteBinderValve.java:192)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:522)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1095)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:672)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1502)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1458)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)
Caused by: org.apache.ws.security.WSSecurityException: The signature or decryption was invalid
    at org.apache.ws.security.processor.SignatureProcessor.verifyXMLSignature(SignatureProcessor.java:393)
    at org.apache.ws.security.processor.SignatureProcessor.handleToken(SignatureProcessor.java:188)
    at org.apache.ws.security.WSSecurityEngine.processSecurityHeader(WSSecurityEngine.java:396)
    at org.apache.ws.security.WSSecurityEngine.processSecurityHeader(WSSecurityEngine.java:304)
    at org.apache.ws.security.WSSecurityEngine.processSecurityHeader(WSSecurityEngine.java:249)
    at org.apache.rampart.handler.WSDoAllReceiver.processBasic(WSDoAllReceiver.java:213)
    ... 33 more
                </Exception>
            </soapenv:Detail>
        </soapenv:Fault>
    </soapenv:Body>
</soapenv:Envelope>

我几乎可以确定我的消息是正确的(我有SoapUI请求正在返回正确的响应,我比较了两个请求。问题是在SoapUI内部我注意到BinarySecurityToken是使用ValueType = X509PKIPathv1发送的。不幸的是,在.NET中,这是不受支持的,我使用的是X509v3)

这是我的自定义MessageEncoder:

public class CustomTextMessageEncoder : MessageEncoder
{
    private CustomTextMessageEncoderFactory factory;
    private XmlWriterSettings writerSettings;
    private string contentType;

    public CustomTextMessageEncoder(CustomTextMessageEncoderFactory factory)
    {
        this.factory = factory;

        writerSettings = new XmlWriterSettings();
        writerSettings.Encoding = Encoding.GetEncoding(factory.CharSet);
        //writerSettings.ConformanceLevel = ConformanceLevel.Fragment;
        //writerSettings.OmitXmlDeclaration = false;
        contentType = string.Format("{0}; charset={1}", this.factory.MediaType, writerSettings.Encoding.HeaderName);
    }

    public override string ContentType
    {
        get
        {
            return contentType;
        }
    }

    public override string MediaType
    {
        get
        {
            return factory.MediaType;
        }
    }

    public override MessageVersion MessageVersion
    {
        get
        {
            return factory.MessageVersion;
        }
    }

    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 = new MemoryStream(msgContents);

        string incoming = Encoding.UTF8.GetString(buffer.Array, buffer.Offset, buffer.Count);
        Console.WriteLine("Incoming message:");
        Console.WriteLine(incoming);
        Console.WriteLine();

        return ReadMessage(stream, int.MaxValue);
    }

    public override Message ReadMessage(Stream stream, int maxSizeOfHeaders, string contentType)
    {
        XmlReader reader = XmlReader.Create(stream);
        var message = Message.CreateMessage(reader, maxSizeOfHeaders, MessageVersion);

        //Debug.WriteLine("RESPONSE:");
        //Debug.WriteLine(message.ToString());
        return message;
    }

    public override ArraySegment<byte> WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset)
    {
        Debug.WriteLine("MESSAGE:");
        Debug.WriteLine(message.ToString());
        MemoryStream ms = new MemoryStream();
        XmlDictionaryWriter w = XmlDictionaryWriter.CreateBinaryWriter(ms);
        message.WriteMessage(w);
        w.Flush();
        ms.Position = 0;
        XmlDictionaryReader r = XmlDictionaryReader.CreateBinaryReader(ms, XmlDictionaryReaderQuotas.Max);
        XmlDocument doc = new XmlDocument();
        doc.PreserveWhitespace = true;
        doc.Load(r);

        XmlNode binarySecurityToken = doc.GetElementsByTagName("o:BinarySecurityToken")[0];
        if (binarySecurityToken != null)
        {
            XmlAttribute attr = doc.CreateAttribute("EncodingType");
            attr.Value = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary";
            if (binarySecurityToken.Attributes != null) binarySecurityToken.Attributes.Append(attr);
        }

        XmlNode SecurityTokenReference = doc.GetElementsByTagName("o:SecurityTokenReference")[0];
        if (SecurityTokenReference != null)
        {
            XmlAttribute attr = doc.CreateAttribute("wsse11", "TokenType", "http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd");
            attr.Value = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3";
            if (SecurityTokenReference.Attributes != null) SecurityTokenReference.Attributes.Append(attr);
        }

        XmlNode wsseReference = doc.GetElementsByTagName("o:Reference")[0];
        if (wsseReference != null)
        {
            XmlAttribute attr = doc.CreateAttribute("ValueType");
            attr.Value = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3";
            if (wsseReference.Attributes != null) wsseReference.Attributes.Append(attr);
        }

        ms = new MemoryStream();
        w = XmlDictionaryWriter.CreateBinaryWriter(ms);
        doc.WriteTo(w);
        w.Flush();
        ms.Position = 0;
        r = XmlDictionaryReader.CreateBinaryReader(ms, XmlDictionaryReaderQuotas.Max);
        Message newMessage = Message.CreateMessage(r, maxMessageSize, message.Version);
        newMessage.Properties.CopyProperties(message.Properties);

        Debug.WriteLine("NEW MESSAGE:");
        Debug.WriteLine(newMessage.ToString());



        ArraySegment<byte> messageBuffer;

        int messageLength;
        using (MemoryStream stream = new MemoryStream())
        {
            using (XmlWriter writer = XmlWriter.Create(stream, writerSettings))
            {
                newMessage.WriteMessage(writer);
            }

            var writeBuffer = stream.ToArray();
            messageBuffer = new ArraySegment<byte>(writeBuffer);

            messageLength = (int)stream.Position;
        }

        int totalLength = messageLength + messageOffset;
        byte[] totalBytes = bufferManager.TakeBuffer(totalLength);
        Array.Copy(messageBuffer.Array, 0, totalBytes, messageOffset, messageLength);

        ArraySegment<byte> byteArray = new ArraySegment<byte>(totalBytes, messageOffset, messageLength);
        return byteArray;
    }


    public override void WriteMessage(Message message, Stream stream)
    {
        XmlWriter writer = XmlWriter.Create(stream, writerSettings);
        message.WriteMessage(writer);
        writer.Close();
    }

    public override bool IsContentTypeSupported(string contentType)
    {
        if (base.IsContentTypeSupported(contentType))
        {
            return true;
        }
        if (contentType.Length == MediaType.Length)
        {
            return contentType.Equals(MediaType, StringComparison.OrdinalIgnoreCase);
        }
        else
        {
            if (contentType.StartsWith(MediaType, StringComparison.OrdinalIgnoreCase)
                && (contentType[MediaType.Length] == ';'))
            {
                return true;
            }
        }
        return false;
    }
}

我的问题是如何更改WriteMessage以正确添加属性而不更改消息格式(空格,换行)。 我需要这个,因为我使用证书签署请求正文,而我当前版本以某种方式改变了消息,因此签名无效。

我的客户端创建代码:

var binding = new CustomBinding();

var sec = (AsymmetricSecurityBindingElement)SecurityBindingElement.CreateMutualCertificateBindingElement(MessageSecurityVersion.WSSecurity10WSTrust13WSSecureConversation13WSSecurityPolicy12BasicSecurityProfile10);
sec.EndpointSupportingTokenParameters.Signed.Add(new UserNameSecurityTokenParameters());
sec.MessageSecurityVersion =
    MessageSecurityVersion.
        WSSecurity10WSTrust13WSSecureConversation13WSSecurityPolicy12BasicSecurityProfile10;
sec.IncludeTimestamp = false;
sec.MessageProtectionOrder = MessageProtectionOrder.EncryptBeforeSign;

binding.Elements.Add(sec);
binding.Elements.Add(new CustomTextMessageBindingElement("UTF-8", "application/soap+xml", MessageVersion.Soap12WSAddressing10));
b.Elements.Add(new HttpsTransportBindingElement());

var client = new QueryDzPortTypeClient(binding, new EndpointAddress(new Uri("https://dz.query.pl/services/QueryDz"), new DnsEndpointIdentity("My service "), new AddressHeaderCollection()));


client.ChannelFactory.Endpoint.Behaviors.Remove<ClientCredentials>();
client.ChannelFactory.Endpoint.Behaviors.Add(new UsernameTokenProfile11Credentials());


client.ClientCredentials.UserName.UserName = _userName;
client.ClientCredentials.UserName.Password = _password;

client.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode =
    X509CertificateValidationMode.None;
client.ClientCredentials.ServiceCertificate.DefaultCertificate = new X509Certificate2(@"C:\Program Files\Microsoft WSE\v2.0\Samples\Sample Test Certificates\Server Public.cer");

client.ClientCredentials.ClientCertificate.Certificate = new X509Certificate2(@"C:\Program Files\Microsoft WSE\v2.0\Samples\Sample Test Certificates\Client Private.pfx", "wse2qs");

client.Endpoint.Contract.ProtectionLevel = ProtectionLevel.Sign;

我已发布消息编码器代码:here

0 个答案:

没有答案