0
votes

I'm trying to setup a WCF client to talk with a service. The connection needs to be https (Transport Security) and we need to do message encryption with a public key. We do not have a client certificate to present.

var binding = new WSHttpBinding();
binding.Security.Mode = SecurityMode.Transport;
// This is failing because the private key for client authentication is not being set
// binding.Security.Mode = SecurityMode.TransportWithMessageCrediential;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
binding.Security.Message.ClientCredentialType = MessageCredentialType.Certificate;

And then to set the public key I'm doing

client.ClientCredentials.ServiceCertificate.DefaultCertificate = certificate;

Where certificate is an X509Certificate2 object with just the public key.

When I do this it does not encrypt the message. Using Fiddler I see the raw text of the message come across the wire and the service gives an error because it is expecting an encrypted message.

Edit: Added comment about TransportWithMessageCrediential

1
Usually encrypting data once is enough for most people. Do you really want both TLS and message security?Hans Kilian
Yes, because it protects against a man in the middle attack. We are sending PII and it is required to have the message encrypted as well.Bert Cotton
I think - for WSHttpBinding - that you can only set Security to Transport or Message. You can't have both. You also seem to confuse encryption with credentials. Credentials don't encrypt the message. They tell the server who the caller is. As for Fiddler being able to see your messages, did you let it install a trusted certificate? Because if you do that, then it can do a man-in-the-middle 'attack'. To protect against that, you can do 'certificate pinning', where you check that the server's certificate is what you expect.Hans Kilian
WSHttpBinding does allow for both (docs.microsoft.com/en-us/dotnet/framework/configure-apps/…). The Fiddle did install the root CA cert, which does allow me to decrypt and see https messages, but it would still show me if it was encrypted using message encryption (hence the need for both).Bert Cotton
"Yes, because it protects against a man in the middle attack". TLS should also be able to do this as it'll require a certificate which must be trusted by the client. A man in the middle will only be successful if the client also trusts the cert of the "Middleman". This should only happen in case of a https inspection by some organisation that has access to the clients cert store.Dominik

1 Answers

0
votes

This is how we have solved this. We are using a CustomBinding to set it all up.

This setups up the binding

private Binding GetBinding()
{
    var binding = new CustomBinding()
    // This could be configured per service
    MessageVersion messageVersion = MessageVersion.Soap12;

    TextMessageEncodingBindingElement messageEncodingElement = new TextMessageEncodingBindingElement(messageVersion, Encoding.UTF8);
    binding.Elements.Add(messageEncodingElement)
    if (/* Encryption is needed */)
    {
        SecurityBindingElement securityElement = securityElement = SecurityBindingElement.CreateAnonymousForCertificateBindingElement();
        securityElement.DefaultAlgorithmSuite = GetEncryptionAlgorithm(); // This will return which algorithm is desired
        securityElement.IncludeTimestamp = false; // This is because our services were returning an error when it was included
        binding.Elements.Add(securityElement);

    HttpsTransportBindingElement httpsTransportElement = new HttpsTransportBindingElement()
    if (/* If Windows Authentication is required */)
        httpsTransportElement.AuthenticationScheme = AuthenticationSchemes.Negotiate;
    binding.Elements.Add(httpsTransportElement)
    return binding;
}

Then to use the client

// By default WCF will check that the DnsName of the certificate matchs the DNS name of the endpoint we need to override this.
// We did want want to require an appliction to use the same cert that is for SSL as for message encryption
EndpointIdentity endpointIdentity = null;
if (endpoint.EncryptionCertificate != null)
    endpointIdentity = EndpointIdentity.CreateDnsIdentity(endpoint.EncryptionCertificate.GetCertificate().GetNameInfo(X509NameType.DnsName, false));
else
    endpointIdentity = EndpointIdentity.CreateDnsIdentity(endpoint.Address);


AddressHeader[] header = null;
using (var client = new portTypeClient(binding, new EndpointAddress(new Uri(address), endpointIdentity, header)))
{
    if (/* Encryption is needed */)
    {
        var certificate = LoadX509Certificate();
        client.ClientCredentials.ServiceCertificate.DefaultCertificate = certificate;
    }
}