Thoughts on BasicHttpBinding, Security and SSL
- BasicHttpBinding, can there be security?. Yes! The point is this binding can be secured and that too with transport level security and restricted to https(SSL) or message level Check this post for details. https://blogs.msdn.com/drnick/archive/2006/06/01/612672.aspx.
- Another common question is about user name security tokens. Please note that user name security tokens cannot be send on the clear. There are quite a few posts on the forums with this quesiton. The point is you need to have a security binding element within the binding you use to send this token across cause WCF doesnt allow clear text tokens to be transfered over the wire. Now this being said, the security may be at the transport or the message layer and not restricted to just message. This means you can deploy something with SSL and use text encoding and send a UserNamePassword token cause the security here is done by the trasnport. The only requirement is that there be a security binding element somewhere in the channel stack so that there is a gurantee that the user name and password is securely tranfered across the wire.
- Do we need valid certs to use SSL? No we dont need a valid cert. The Sample below shows how you can use a self made cert to setup SSL on a port using netsh on vista and send a username token. Please note this is not production code and only shows the capabilites. Ideally you require IIS to be setup with a valid cert and make sure your requested name matches the cert.
This sample basically shows the use of the PermissiveCertificatePolicy that enables use of self made certs.
You need to setup SSL for your security element. Username tokens cannot be send clear ont he wire.
If you are on vista setup the certificate for SSL using netsh with something like this.
C:\Windows\system32>netsh http add sslcert ipport=0.0.0.0:8080 certhash=05eef6e118e516869a75f96057a2310ecdb8a44f appid={00112233-4455-6677-8899-AABBCC
DDEEFF}
SSL Certificate successfully added
The code blow shows a self hosted service with a permissive certificate policy so that you can use certs made using makecert etc.
using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Security.Cryptography.X509Certificates;
using System.Net;
using System.IdentityModel.Selectors;
namespace SimpleUNP
{
[ServiceContract]
interface IService
{
[OperationContract]
string Do();
}
public class ServiceImplementation : IService
{
public string Do()
{
return "Hello Service";
}
}
public class CustomUNPValidator : UserNamePasswordValidator
{
public override void Validate(string userName, string password)
{
Console.BackgroundColor = ConsoleColor.Red;
Console.WriteLine("Username at the service : " + userName);
Console.ResetColor();
}
}
class Program
{
static void Main(string[] args)
{
string addr = "https://localhost:8080/MyService";
Uri[] baseAddrs = new Uri[] { new Uri(addr) };
BasicHttpBinding binding = new BasicHttpBinding();
binding.Security.Mode = BasicHttpSecurityMode.TransportWithMessageCredential;
binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;
using (ServiceHost sh = new ServiceHost(typeof(ServiceImplementation), baseAddrs))
{
sh.Description.Behaviors.Find<ServiceDebugBehavior>().IncludeExceptionDetailInFaults = true;
sh.Credentials.UserNameAuthentication.UserNamePasswordValidationMode = System.ServiceModel.Security.UserNamePasswordValidationMode.Custom;
sh.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = new CustomUNPValidator();
sh.AddServiceEndpoint(typeof(IService), binding, "");
sh.Open();
Console.WriteLine("Host listening on " + sh.BaseAddresses[0].AbsolutePath);
try
{
// WARNING: This code is only needed for test certificates such as those created by makecert. It is
// not recommended for production code.
PermissiveCertificatePolicy.Enact("CN=localhost");
ChannelFactory<IService> cf = new ChannelFactory<IService>(binding, addr);
cf.Credentials.UserName.UserName = "TestUsername";
cf.Credentials.UserName.Password = "";
IService proxy = cf.CreateChannel();
Console.WriteLine(proxy.Do());
}
catch (Exception ex)
{
}
}
}
}
// WARNING: This code is only needed for test certificates such as those created by makecert. It is
// not recommended for production code.
class PermissiveCertificatePolicy
{
string subjectName;
static PermissiveCertificatePolicy currentPolicy;
PermissiveCertificatePolicy(string subjectName)
{
this.subjectName = subjectName;
ServicePointManager.ServerCertificateValidationCallback +=
new System.Net.Security.RemoteCertificateValidationCallback(RemoteCertValidate);
}
public static void Enact(string subjectName)
{
currentPolicy = new PermissiveCertificatePolicy(subjectName);
}
bool RemoteCertValidate(object sender, X509Certificate cert, X509Chain chain, System.Net.Security.SslPolicyErrors error)
{
if (cert.Subject == subjectName)
{
return true;
}
return false;
}
}
}