在我的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