我正在尝试使用WCF客户端连接到基于Java的Web服务
证书我已经提供(自签名)在SOAPUI中完美地工作。
这是我的设置:
但是,我在使用WCF客户端时遇到问题。
我的app.config
<bindings>
<customBinding>
<binding name="Example_TestBinding">
<security defaultAlgorithmSuite="TripleDesRsa15"
authenticationMode="MutualCertificate"
requireDerivedKeys="false"
includeTimestamp="false"
messageProtectionOrder="SignBeforeEncrypt"
messageSecurityVersion="WSSecurity10WSTrust13WSSecureConversation13WSSecurityPolicy12BasicSecurityProfile10"
requireSignatureConfirmation="false">
<localClientSettings detectReplays="true"/>
<localServiceSettings detectReplays="true"/>
</security>
<textMessageEncoding messageVersion="Soap11"/>
<httpsTransport authenticationScheme="Basic" manualAddressing="false" maxReceivedMessageSize="524288000" transferMode="Buffered"/>
</binding>
</customBinding>
</bindings>
<client>
<endpoint
address="https://blabla.hana.ondemand.com/Example_Test"
binding="customBinding"
bindingConfiguration="Example_TestBinding"
contract="WebServiceTest.Example_Test"
name="Example_Test"
/>
</client>
使用Keystore Explorer我从JKS导出两个证书:
网络服务电话:
var client = new Example_TestClient();
client.ClientCredentials.UserName.UserName = "user";
client.ClientCredentials.UserName.Password = "pass";
X509Certificate2 certClient = new X509Certificate2(certClientPath, certClientPassword);
client.ClientCredentials.ClientCertificate.Certificate = certClient;
X509Certificate2 certService= new X509Certificate2(certServicePath);
client.ClientCredentials.ServiceCertificate.DefaultCertificate = certService;
var response = client.Example_Test(requestObj);
请求完全到达服务器,但似乎WCF不理解响应,因为我得到此异常:
"The EncryptedKey clause was not wrapped with the required
encryption token 'System.IdentityModel.Tokens.X509SecurityToken'."
at System.ServiceModel.Security.WSSecurityJan2004.WrappedKeyTokenEntry.CreateWrappedKeyToken(String id, String encryptionMethod, String carriedKeyName, SecurityKeyIdentifier unwrappingTokenIdentifier, Byte[] wrappedKey, SecurityTokenResolver tokenResolver)\r\n ...
服务跟踪给出:
The security protocol cannot verify the incoming message
UPDATE1:通过使用相同的证书进行签名和加密来简化任务。同样的消息。
UPDATE2:我编写了CustomTextMessageEncoder,我在其中手动解密消息体并且它可以正常工作。但是,在ReadMessage中返回它仍会引发错误。
public override Message ReadMessage(ArraySegment<byte> buffer, BufferManager bufferManager, string contentType)
{
var msgContents = new byte[buffer.Count];
Array.Copy(buffer.Array, buffer.Offset, msgContents, 0, msgContents.Length);
bufferManager.ReturnBuffer(buffer.Array);
var message = Encoding.UTF8.GetString(msgContents);
//return ReadMessage(Decryptor.DecryptBody(message), int.MaxValue);
var stream = new MemoryStream(Encoding.UTF8.GetBytes(message));
return ReadMessage(stream, int.MaxValue);
}
public static MemoryStream DecryptBody(string xmlResponse)
{
X509Certificate2 cert = new X509Certificate2(clientCertPath, certPass);
SymmetricAlgorithm algorithm = new TripleDESCryptoServiceProvider();
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.PreserveWhitespace = true;
xmlDoc.LoadXml(xmlResponse);
XmlElement encryptedKeyElement = xmlDoc.GetElementsByTagName("EncryptedKey", XmlEncryptionStrings.Namespace)[0] as XmlElement;
XmlElement keyCipherValueElement = encryptedKeyElement.GetElementsByTagName("CipherValue", XmlEncryptionStrings.Namespace)[0] as XmlElement;
XmlElement encryptedElement = xmlDoc.GetElementsByTagName("EncryptedData", XmlEncryptionStrings.Namespace)[0] as XmlElement;
var key = Convert.FromBase64String(keyCipherValueElement.InnerText);
EncryptedData edElement = new EncryptedData();
edElement.LoadXml(encryptedElement);
EncryptedXml exml = new EncryptedXml();
algorithm.Key = (cert.PrivateKey as RSACryptoServiceProvider).Decrypt(key, false);
byte[] rgbOutput = exml.DecryptData(edElement, algorithm);
exml.ReplaceData(encryptedElement, rgbOutput);
//var body = Encoding.UTF8.GetString(rgbOutput);
MemoryStream ms = new MemoryStream();
xmlDoc.Save(ms);
return ms;
}
我把这个问题留给了我项目的最后冲刺,最后回到了它。 这是证书问题。我使用KeyStore Explorer生成了基于Java的Web服务提供的自签名证书。两个证书都缺少一个重要部分:
Subject Key Identifier
一旦重新生成,WCF就能够在不使用编码器的情况下对其进行解密。
我也不得不:
我从代码中删除了所有配置(客户端用户名和密码除外)并放在app.config中。这是完整的配置:
<system.serviceModel>
<bindings>
<customBinding>
<binding name="Example_TestBinding">
<security
defaultAlgorithmSuite="TripleDesRsa15"
authenticationMode="MutualCertificate"
requireDerivedKeys="false"
includeTimestamp="true"
messageProtectionOrder="SignBeforeEncrypt"
messageSecurityVersion="WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10"
requireSignatureConfirmation="false"
allowSerializedSigningTokenOnReply="true"
>
</security>
<textMessageEncoding messageVersion="Soap11"/>
<httpsTransport authenticationScheme="Basic"
manualAddressing="false"
maxReceivedMessageSize="524288000"
transferMode="Buffered"/>
</binding>
</customBinding>
</bindings>
<client>
<endpoint address="https://klaatuveratanecto.com/cxf/Example_TestBinding"
behaviorConfiguration="endpointCredentialBehavior"
binding="customBinding"
bindingConfiguration="Example_TestBinding"
contract="WebServiceTest.Example_Test"
name="Example_Test">
<identity>
<dns value="test.service.klaatuveratanecto.com"/>
</identity>
</endpoint>
</client>
<behaviors>
<endpointBehaviors>
<behavior name="endpointCredentialBehavior">
<clientCredentials>
<serviceCertificate>
<defaultCertificate findValue="test.service.klaatuveratanecto.com"
storeLocation="CurrentUser"
storeName="Root"
x509FindType="FindBySubjectName" />
</serviceCertificate>
<clientCertificate findValue="test.client.klaatuveratanecto.com"
storeLocation="CurrentUser"
storeName="My"
x509FindType="FindBySubjectName" />
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
我是怎么到那儿的仔细看堆栈跟踪:
Server stack trace:
at System.ServiceModel.Security.WSSecurityJan2004.WrappedKeyTokenEntry.CreateWrappedKeyToken(String id, String encryptionMethod, String carriedKeyName, SecurityKeyIdentifier unwrappingTokenIdentifier, Byte[] wrappedKey, SecurityTokenResolver tokenResolver)
at System.ServiceModel.Security.WSSecurityJan2004.WrappedKeyTokenEntry.ReadTokenCore(XmlDictionaryReader reader, SecurityTokenResolver tokenResolver)
at System.ServiceModel.Security.WSSecurityTokenSerializer.ReadTokenCore(XmlReader reader, SecurityTokenResolver tokenResolver)
at System.IdentityModel.Selectors.SecurityTokenSerializer.ReadToken(XmlReader reader, SecurityTokenResolver tokenResolver)
at System.ServiceModel.Security.WSSecurityOneDotZeroReceiveSecurityHeader.DecryptWrappedKey(XmlDictionaryReader reader)
at System.ServiceModel.Security.ReceiveSecurityHeader.ReadEncryptedKey(XmlDictionaryReader reader, Boolean processReferenceListIfPresent)
at System.ServiceModel.Security.ReceiveSecurityHeader.ExecuteFullPass(XmlDictionaryReader reader)
at System.ServiceModel.Security.StrictModeSecurityHeaderElementInferenceEngine.ExecuteProcessingPasses(ReceiveSecurityHeader securityHeader, XmlDictionaryReader reader)
at System.ServiceModel.Security.ReceiveSecurityHeader.Process(TimeSpan timeout, ChannelBinding channelBinding, ExtendedProtectionPolicy extendedProtectionPolicy)
at System.ServiceModel.Security.MessageSecurityProtocol.ProcessSecurityHeader(ReceiveSecurityHeader securityHeader, Message& message, SecurityToken requiredSigningToken, TimeSpan timeout, SecurityProtocolCorrelationState[] correlationStates)
at System.ServiceModel.Security.AsymmetricSecurityProtocol.VerifyIncomingMessageCore(Message& message, String actor, TimeSpan timeout, SecurityProtocolCorrelationState[] correlationStates)
at System.ServiceModel.Security.MessageSecurityProtocol.VerifyIncomingMessage(Message& message, TimeSpan timeout, SecurityProtocolCorrelationState[] correlationStates)
at System.ServiceModel.Channels.SecurityChannelFactory`1.SecurityRequestChannel.ProcessReply(Message reply, SecurityProtocolCorrelationState correlationState, TimeSpan timeout)
at System.ServiceModel.Channels.SecurityChannelFactory`1.SecurityRequestChannel.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Dispatcher.RequestChannelBinder.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
我在JetBrains dotPeek的帮助下调试了CreateWrappedKeyToken方法,并看到它试图从证书中读取原始SKI并且它没有找到它。