A lot of Wcf users encounter the below error when consuming a secured non-Wcf web services:
Understanding the problem
When we configure the service x.509 certificate in the client the latter sees this certificate as the service identity. So whenever we interact with this service the client verifies that this certificate is still the service identity, and if not it assumes some sort of phishing occur and drops the connection.
This is our client config:
We have declared the above certificate here since we want Wcf to use it for encryption and to verify signature. Wcf does all this and also something else: it assumes this is the service “identity”. We’ll see how this afefcts us later.
This is our client request:
The client uses the service public key to create a session key which is used to encrypt the request body. Only the owner of the matching private key knows how to decrypt this and since this owner is the server the client gets the authentication it wants.
This is the service response:
The service signs the response message. Anyone who has the service public key can now verify that the service is the one who sent this response and not some MITM. And since our client has the server public key (it’s the one we configured in the config, which is the same one the client used for encryption) the client can now validate the signature source.
So when do we get this “signed with a different token” error?
We can see in the service response a “binarySecurityToken” element. The service uses it to notify the client which certificate it used for signature. The content of this element is just a base-64 encoded certificate, so the client can easily extract it and compare to the certificate it has configured for the service. If they are not the same we get this exception.
Why must they be the same?
Remember that when we configure the service certificate on the client, Wcf treats it as the service identity. When a response comes back the client expects the service to prove its identity by signing the response with its key. If the response is signed – but using a different key – Wcf throws this authentication error since service identity has not been proved.
I am not using any encryption. Why do I get this error?
Even if you use ProtectionLevel.Sign, Wcf still requires you configure a service certificate. Since the service signs the response Wcf will compare the signing token to the certificate you configured.
If there is indeed a requirement for the service to use a different signing and encryption certificates, what we would like to do is to configure both of them on the client, so Wcf would know that both of them can authenticate the service. Unfortunately Wcf only allows us one slot of service certificate per configuration. So we need to look in other places.
If the scenario is Wcf-2-Wcf, and we are ready to use the proprietary duplex communication, then there is a way to use separate X.509 certificates for signing and encryption.
If we use ProtectionLevel.Sign, meaning we only sign messages but do not encrypt them, then the service certificate which we configure on the client is only used for the purpose of validating the response signature. So we are free to change it to be the actual signing certificate. In some cases we may not know what this certificate is. Apart from asking the service author to provide it we can examine the response (as above) and extract the certificate from the binary security token (which may not always exist though).
This is a workaround that reduces the security level of the communication. Use it only if you have other means to verify the service apart form the signature or if you are willing to have reduced security.
Step 1: Implement a custom message encoder which removes from the response the Envelope/Header/Security element all together.
Step 2: Configre EnableUnsecuredResponse to true:
This is a Wcf 4.0 new feature but it is also available in a patch to Wcf 3.5 SP1. Strive to use 4.0 if you can since some users have problems with the patch.
This solution requires to use a custom binding. If you need help converting your wsHttpbinding to a custom one use the binding converter.
The implication of this workaround is that your client will not verify the service identity at all – this is usually not desired from security perspective.