I'm trying to use WCF client to connect to Java based web services
Certificates I have been provided (self-signed) work perfectly in SOAPUI.
Here is my setup:
However, I'm having problems using WCF client.
My 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>
Using Keystore Explorer I export both certificates from JKS as:
- public_test_hci_cert.cer
- test_soap_ui.p12
Web service call:
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);
The request arrives perfectly at the server but it seems that WCF doesn't understand the response since I get this exception:
"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 ...
Service Trace gives:
The security protocol cannot verify the incoming message
UPDATE1: simplified the task by using the same certificate for signing and encryption. Same message.
UPDATE2: I wrote CustomTextMessageEncoder where I manually decrypt the message body and it works. However returning it in ReadMessage still throws the error.
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; }
1 Answers
Answers 1
I left this problem for the final sprint in my project and finally got back to it.
It is certificate problem. The self-signed certificates I was provided by Java based web service was generated with KeyStore Explorer. Both certificates were missing an important part:
Subject Key Identifier
Once regenerated WCF was able to decrypt it without using encoders.
Also I had to:
- Install service certificate in the Trusted Root CA (user)
- Set Web Services to reply with Timestamp
I removed all config from the code (except client username and password) and placed in the app.config. Here is the complete 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>
How did I get there. Well looking at the stack trace:
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)
I debugged CreateWrappedKeyToken method with help of JetBrains dotPeek and saw that it tries to read raw SKI from the certificate and it's not finding it.
0 comments:
Post a Comment