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:
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;
}
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:
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With