Search code examples
wcfnettcpbinding

WCF over TCP with username authentication and no certificates


I'm using WCF for communication between various .NET applications. These services are all on the same private subnet, so I'd like to avoid the complexity and performance overhead of encryption and certificates. I do, however, need basic username/password support since the requests are all authenticated against our custom MembershipProvider.

We are currently using HTTP with Clear Username Binding and that is working well. However, I would like to use TCP to improve performance. Is it possible to do simple username/password authentication (the way Clear Username Binding does) over NetTcpBinding without having to use certificates, encryption, etc?


Solution

  • The solution I ended up going with was modifying Clear Username Binding to use TCP for transport and binary message encoding. I got the idea from a series of comments on the author's blog. The complete code for my binding is below:

    using System;
    using System.Configuration;
    using System.Net.Security;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Configuration;
    
    namespace ClearTcpBinding
    {
        public class ClearTcpBinding : CustomBinding
        {
            private long _maxReceivedMessageSize = 65536;
    
            public void SetMaxReceivedMessageSize(long value)
            {
                _maxReceivedMessageSize = value;
            }
    
            public override BindingElementCollection CreateBindingElements()
            {
                var res = new BindingElementCollection
                              {
                                  new BinaryMessageEncodingBindingElement {MessageVersion = MessageVersion.Soap12WSAddressing10},
                                  SecurityBindingElement.CreateUserNameOverTransportBindingElement(),
                                  new AutoSecuredTcpTransportElement {MaxReceivedMessageSize = _maxReceivedMessageSize}
                              };
                return res;
            }
    
            public override string Scheme { get { return "net.tcp"; } }
        }
    
        public class ClearTcpBindingElement : StandardBindingElement
        {
            private ConfigurationPropertyCollection _properties;
    
            protected override void OnApplyConfiguration(Binding binding)
            {
                var b = (ClearTcpBinding)binding;
                b.SetMaxReceivedMessageSize(Convert.ToInt64(MaxReceivedMessageSize));
            }
    
            protected override Type BindingElementType
            {
                get { return typeof(ClearTcpBinding); }
            }
    
            protected override ConfigurationPropertyCollection Properties
            {
                get
                {
                    if (_properties == null)
                    {
                        var properties = base.Properties;
                        properties.Add(new ConfigurationProperty("maxReceivedMessageSize", typeof(string), "65536"));
                        _properties = properties;
                    }
                    return _properties;
                }
            }
    
            public string MaxReceivedMessageSize
            {
                get { return (string)base["maxReceivedMessageSize"]; }
                set { base["maxReceivedMessageSize"] = value; }
            }
        }
    
        public class ClearTcpCollectionElement
            : StandardBindingCollectionElement<ClearTcpBinding, ClearTcpBindingElement>
        {
        }
    
        public class AutoSecuredTcpTransportElement : TcpTransportBindingElement, ITransportTokenAssertionProvider
        {
            public override T GetProperty<T>(BindingContext context)
            {
                if (typeof(T) == typeof(ISecurityCapabilities))
                    return (T)(ISecurityCapabilities)new AutoSecuredTcpSecurityCapabilities();
                return base.GetProperty<T>(context);
            }
    
            public System.Xml.XmlElement GetTransportTokenAssertion()
            {
                return null;
            }
        }
    
        public class AutoSecuredTcpSecurityCapabilities : ISecurityCapabilities
        {
            public ProtectionLevel SupportedRequestProtectionLevel { get { return ProtectionLevel.EncryptAndSign; } }
            public ProtectionLevel SupportedResponseProtectionLevel { get { return ProtectionLevel.EncryptAndSign; } }
            public bool SupportsClientAuthentication { get { return false; } }
            public bool SupportsClientWindowsIdentity { get { return false; } }
            public bool SupportsServerAuthentication { get { return true; } }
        }
    }