Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WCF : The EncryptedKey clause was not wrapped with the required encryption token 'System.IdentityModel.Tokens.X509SecurityToken'

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:

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

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;
    } 
like image 222
Artur Kędzior Avatar asked Oct 11 '17 11:10

Artur Kędzior


1 Answers

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

enter image description here

Once regenerated WCF was able to decrypt it without using encoders.

Also I had to:

  1. Install service certificate in the Trusted Root CA (user)
  2. 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.

like image 179
Artur Kędzior Avatar answered Nov 17 '22 23:11

Artur Kędzior