Why is WCF service not invoke when security is enabled on Async operations - wcf

When Security feature is enabled for Authencation using X509 certificate, Connecation to the server is not established. I find this issue when the client makes Async operation call. The code below works for synchronous operation. For instance if the call is made TrainHealth instead of BeginTrainHealth, the code works fine. As I understand, security is independent of whether operations are sync or async.
Any help on why this is not working is appreciated.
Contract :
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(Namespace = "http://www.bane.dk/services/fbane/2014/02/20", ConfigurationName = "Alstom.SmartBus.ESBBridge.ExternalIOPlugins.TrainData.TrainDataPort")]
public interface IStudentService
{
[System.ServiceModel.OperationContractAttribute(IsOneWay = true, AsyncPattern = false, Action = "TrainHealth")]
[System.ServiceModel.XmlSerializerFormatAttribute()]
void TrainHealth(int test);
[System.ServiceModel.OperationContractAttribute(IsOneWay = true, AsyncPattern = true, Action = "TrainHealth")]
[System.ServiceModel.XmlSerializerFormatAttribute()]
System.IAsyncResult BeginTrainHealth(int test, System.AsyncCallback callback, object asyncState);
void EndTrainHealth(System.IAsyncResult result);
}
Server Code :
class Program
{
static void Main(string[] args)
{
ServiceHost studentServiceHost = null;
try
{
//Base Address for StudentService
Uri httpBaseAddress = new Uri("https://10.107.64.33:5060/StudentService");
//Instantiate ServiceHost
studentServiceHost = new ServiceHost(typeof(StudentService), httpBaseAddress);
CustomBinding binding = CreateBinding();
ServiceEndpoint endpoint_GD = studentServiceHost.AddServiceEndpoint(typeof(IStudentService), binding, httpBaseAddress);
//Add Endpoint to Host
studentServiceHost.AddServiceEndpoint(typeof(IStudentService), binding, httpBaseAddress + "mex");
//Metadata Exchange
ServiceMetadataBehavior serviceBehavior = new ServiceMetadataBehavior();
serviceBehavior.HttpsGetEnabled = true;
studentServiceHost.Description.Behaviors.Add(serviceBehavior);
var behavior = studentServiceHost.Description.Behaviors.Find<ServiceDebugBehavior>();
behavior.IncludeExceptionDetailInFaults = true;
studentServiceHost.Credentials.ClientCertificate.Authentication.CertificateValidationMode = System.ServiceModel.Security.X509CertificateValidationMode.PeerTrust;
studentServiceHost.Credentials.ClientCertificate.Authentication.TrustedStoreLocation = StoreLocation.LocalMachine;
studentServiceHost.Credentials.ServiceCertificate.SetCertificate(System.Security.Cryptography.X509Certificates.StoreLocation.LocalMachine, System.Security.Cryptography.X509Certificates.StoreName.My, System.Security.Cryptography.X509Certificates.X509FindType.FindBySubjectName, "10.107.64.33");
ServiceSecurityAuditBehavior newAudit = new ServiceSecurityAuditBehavior();
newAudit.AuditLogLocation = AuditLogLocation.Application;
newAudit.MessageAuthenticationAuditLevel = AuditLevel.SuccessOrFailure;
newAudit.ServiceAuthorizationAuditLevel = AuditLevel.SuccessOrFailure;
newAudit.SuppressAuditFailure = false;
studentServiceHost.Description.Behaviors.Remove<ServiceSecurityAuditBehavior>();
studentServiceHost.Description.Behaviors.Add(newAudit);
studentServiceHost.Open();
Console.WriteLine("Service is live now at : {0}", httpBaseAddress);
Console.ReadKey();
}
catch (Exception ex)
{
studentServiceHost = null;
Console.WriteLine("There is an issue with StudentService" + ex.Message);
Console.ReadKey();
}
}
public static CustomBinding CreateBinding()
{
CustomBinding binding = new CustomBinding();
binding.Elements.Add(new ReliableSessionBindingElement());
TransportSecurityBindingElement sec = new TransportSecurityBindingElement();
binding.Elements.Add(sec);
binding.Elements.Add(new TextMessageEncodingBindingElement(MessageVersion.Soap11WSAddressing10, Encoding.UTF8));
binding.Elements.Add(new HttpsTransportBindingElement()
{
RequireClientCertificate = true
});
return binding;
}
}
Client Code :
class Program
{
static ChannelFactory<IStudentService> runTrainSvcCF = null;
static IStudentService runTrainSvcProxy;
static void Main(string[] args)
{
StartClient();
}
private static void StartClient()
{
EndpointAddress endPoint = new EndpointAddress(new Uri("https://10.107.64.33:5060/StudentService"), EndpointIdentity.CreateDnsIdentity("10.107.64.34"));
CustomBinding binding = CreateBinding();
runTrainSvcCF = new ChannelFactory<IStudentService>(binding, endPoint);
System.ServiceModel.Description.ClientCredentials credentials = runTrainSvcCF.Credentials;
credentials.ClientCertificate.SetCertificate(System.Security.Cryptography.X509Certificates.StoreLocation.CurrentUser,
System.Security.Cryptography.X509Certificates.StoreName.My,
System.Security.Cryptography.X509Certificates.X509FindType.FindBySubjectName,
"10.107.64.34");
ServicePointManager.MaxServicePointIdleTime = 0;
credentials.ServiceCertificate.Authentication.CertificateValidationMode = System.ServiceModel.Security.X509CertificateValidationMode.PeerTrust;
ServicePointManager.ServerCertificateValidationCallback += new System.Net.Security.RemoteCertificateValidationCallback(ValidateRemoteCertificate);
runTrainSvcProxy = runTrainSvcCF.CreateChannel();
try
{
runTrainSvcProxy.BeginTrainHealth(88, null, null);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
private static bool ValidateRemoteCertificate(object sender, System.Security.Cryptography.X509Certificates.X509Certificate cert,
System.Security.Cryptography.X509Certificates.X509Chain chain, System.Net.Security.SslPolicyErrors policyErrors)
{
return true;
}
public static CustomBinding CreateBinding()
{
CustomBinding binding = new CustomBinding();
binding.Elements.Add(new ReliableSessionBindingElement());
binding.Elements.Add(new TransportSecurityBindingElement());
binding.Elements.Add(new TextMessageEncodingBindingElement(MessageVersion.Soap11WSAddressing10, Encoding.UTF8));
binding.Elements.Add(new HttpsTransportBindingElement()
{
RequireClientCertificate = true
});
return binding;
}
}

Related

HttpClient does not work with dev certifcate

I want to use HttpClient to make requests using a certificate.
I generated a dev certificate using the following command
dotnet dev-certs https -ep dev_cert.pfx -p 1234
So I created an API to use that certificate. Here is the implementation.
public static class AuthenticationExtension
{
public static void ConfigureAuthetication(this IServiceCollection services)
{
services.AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme)
.AddCertificate(options =>
{
var cert = new X509Certificate2(#"D:\dev_cert.pfx", "1234");
options.AllowedCertificateTypes = CertificateTypes.All;
options.ChainTrustValidationMode = X509ChainTrustMode.CustomRootTrust;
options.CustomTrustStore = new X509Certificate2Collection { cert };
options.RevocationMode = X509RevocationMode.NoCheck;
options.ValidateCertificateUse = false;
options.Events = new CertificateAuthenticationEvents
{
OnCertificateValidated = context =>
{
var validationService = context.HttpContext.RequestServices.GetService<ClientCertificateValidationService>();
if (validationService != null && validationService.ValidateCertificate(context.ClientCertificate))
{
Console.WriteLine("Success");
context.Success();
}
else
{
Console.WriteLine("invalid cert");
context.Fail("invalid cert");
}
return Task.CompletedTask;
},
OnChallenge = context =>
{
return Task.CompletedTask;
},
OnAuthenticationFailed = context =>
{
Console.WriteLine("Failed");
return Task.CompletedTask;
}
};
});
services.AddAuthorization();
}
}
I added the Authorize attribute in the controller method
[HttpGet(Name = "GetWeatherForecast")]
[Authorize]
public IEnumerable<WeatherForecast> Get()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
I tested the request using Postman and it worked as expected.
Then, I created a console app to make the request using HttpClient. It failed always with the Forbidden Status code.
So I tested using the obsolete WebRequest. And It worked as expected.
Here is the implementation for that
using System.Net;
using System.Security.Cryptography.X509Certificates;
var handler = new HttpClientHandler();
X509Certificate2Collection certificates = new X509Certificate2Collection();
certificates.Import(#"D:\dev_cert.pfx", "1234", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
handler.ClientCertificates.AddRange(certificates);
using var client = new HttpClient(handler);
client.BaseAddress = new Uri("https://localhost:7148/");
try
{
var response = await client.GetAsync("weatherforecast/");
if (response.IsSuccessStatusCode)
{
var responseContent = await response.Content.ReadAsStringAsync();
Console.WriteLine(responseContent);
}
else
{
Console.WriteLine($"Failed {response.StatusCode}");
}
}
catch (Exception ex)
{
Console.WriteLine($"Failed {ex}");
}
string host = #"https://localhost:7148/weatherforecast";
try
{
ServicePointManager.ServerCertificateValidationCallback = (a, b, c, d) => true;
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(host);
req.AllowAutoRedirect = true;
req.ClientCertificates = certificates;
req.Method = "GET";
WebResponse resp = req.GetResponse();
Stream stream = resp.GetResponseStream();
using (StreamReader reader = new StreamReader(stream))
{
string line = reader.ReadLine();
while (line != null)
{
Console.WriteLine(line);
line = reader.ReadLine();
}
}
stream.Close();
}
catch (Exception e)
{
Console.WriteLine(e);
}
Console.ReadLine();
My question is... Since the WebRequest is obsolete what I can do to make the HttpClient work?
Thank you in Advance!

Can't get WCF WebCannelFactory BeforeSendRequest to work

The first code block returns a good request. The second returns a fault. Please advise.
The first code block returns information about a customer, and it uses OAuth2. The second block uses an interceptor. The problem is that when I use a WebChannelFactory and look at my request in trace, it shows (compared to a good request) that everything (the body of the request) is put into the message block. In a good request, the body of my request, is put into the soap message.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Text;
using System.Threading.Tasks;
namespace Ahcccs.Isd.Aes.Breaz.Core.WCF.SOAP
{
public class ConsumeGetVendorLookup
{
public void Consume(string wso2token)
{
Header header = new Header();
header.Type = "?";
header.ReturnCode = "?";
header.Requestor = "?";
header.Recipient = "?";
header.Date = "?";
Requestor requestor = new Requestor();
requestor.UserID = "";
requestor.Password = "";
header.Subject = requestor;
Payload payload = new Payload();
GetVendorCustomerIn getVendorCustomer = new GetVendorCustomerIn();
getVendorCustomer.IncludeGeneralInfo = true;
getVendorCustomer.IncludeHeadquarters = true;
getVendorCustomer.IncludePrenote_EFT = true;
getVendorCustomer.IncludePrenote_EFTSpecified = true;
getVendorCustomer.Vendor_Customer = "{our customer number}";
payload.GetVendorCustomerIn = getVendorCustomer;
AdvMessage advMessage = new AdvMessage();
advMessage.Header = header;
advMessage.Payload = payload;
GetVendorCustomer customer = new GetVendorCustomer();
customer.AdvMessage = advMessage;
VendorServicesClient wcfClient = new VendorServicesClient();
var s = wcfClient.State;
wcfClient.Open();
using (new OperationContextScope(wcfClient.InnerChannel))
{
WebProxy wproxy = new WebProxy(new Uri("http://{our proxy server and port}"), true);
wproxy.BypassProxyOnLocal = true;
wproxy.UseDefaultCredentials = true;
WebRequest.DefaultWebProxy = wproxy;
// Add a HTTP Header to an outgoing request
HttpRequestMessageProperty requestMessage = new HttpRequestMessageProperty();
requestMessage.Headers["Authorization"] = " Bearer " + wso2token;
OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = requestMessage;
var result = wcfClient.getVendorCustomer(customer);
}
}
}
}
This one returns a fault:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Web;
using System.Text;
using System.Threading.Tasks;
namespace Ahcccs.Isd.Aes.Breaz.Core.WCF.SOAP
{
public class ConsumeGetVendorLookup
{
public void Consume(string wso2token)
{
Header header = new Header();
header.Type = "?";
header.ReturnCode = "?";
header.Requestor = "?";
header.Recipient = "?";
header.Date = "?";
Requestor requestor = new Requestor();
requestor.UserID = "";
requestor.Password = "";
header.Subject = requestor;
Payload payload = new Payload();
GetVendorCustomerIn getVendorCustomer = new GetVendorCustomerIn();
getVendorCustomer.IncludeGeneralInfo = true;
getVendorCustomer.IncludeHeadquarters = true;
getVendorCustomer.IncludePrenote_EFT = true;
getVendorCustomer.IncludePrenote_EFTSpecified = true;
getVendorCustomer.Vendor_Customer = "{our customer number}";
payload.GetVendorCustomerIn = getVendorCustomer;
AdvMessage advMessage = new AdvMessage();
advMessage.Header = header;
advMessage.Payload = payload;
GetVendorCustomer customer = new GetVendorCustomer();
customer.AdvMessage = advMessage;
getVendorCustomerRequest customerReq = new getVendorCustomerRequest();
customerReq.getVendorCustomer = customer;
//VendorServicesClient wcfClient = new VendorServicesClient();
//var s = wcfClient.State;
//wcfClient.Open();
var wcfClient = new WebChannelFactory<VendorServicesChannel>(
new Uri("{the target uri}"));
//var s = wcfClient.State;
//wcfClient.Open();
wcfClient.Endpoint.EndpointBehaviors.Add(new AuthenticationHeaderBehavior("txtUser", "txtPass", wso2token));
//using (new OperationContextScope(wcfClient.InnerChannel))
//{
// WebProxy wproxy = new WebProxy(new Uri("our proxy server and port"), true);
// wproxy.BypassProxyOnLocal = true;
// wproxy.UseDefaultCredentials = true;
// WebRequest.DefaultWebProxy = wproxy;
// // Add a HTTP Header to an outgoing request
// HttpRequestMessageProperty requestMessage = new HttpRequestMessageProperty();
// requestMessage.Headers["Authorization"] = " Bearer " + wso2token;
// OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = requestMessage;
// var result = wcfClient.getVendorCustomer(customer);
//}
var proxy = wcfClient.CreateChannel();
using ((IDisposable)proxy)
using (OperationContextScope c = new OperationContextScope((IContextChannel)proxy))
{
//WebProxy wproxy = new WebProxy(new Uri("our proxy server and port"), true);
//wproxy.BypassProxyOnLocal = true;
//wproxy.UseDefaultCredentials = true;
//WebRequest.DefaultWebProxy = wproxy;
//Add a HTTP Header to an outgoing request
//HttpRequestMessageProperty requestMessage = new HttpRequestMessageProperty();
//requestMessage.Headers["Authorization"] = " Bearer " + wso2token;
//OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = requestMessage;
var result = proxy.getVendorCustomer(customerReq);
}
}
}
public class AuthenticationHeader : IClientMessageInspector
{
#region Implementation of IClientMessageInspector
string itsUser;
string itsPass;
string itsToken;
public AuthenticationHeader(string user, string pass, string token)
{
itsUser = user;
itsPass = pass;
itsToken = token;
}
public object BeforeSendRequest(ref Message request,
IClientChannel channel)
{
//HttpRequestMessageProperty hrmp = request.Properties["httpRequest"] as HttpRequestMessageProperty;
//string encoded = System.Convert.ToBase64String(System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes(itsUser + ":" + itsPass));
//hrmp.Headers.Add("Authorization", "Basic " + encoded);
//return request;
//HttpRequestMessageProperty hrmp = request.Properties["httpRequest"] as HttpRequestMessageProperty;
//hrmp.Headers.Add("Authorization", " Bearer " + itsToken);
//HttpRequestMessageProperty requestMessage = new HttpRequestMessageProperty();
//requestMessage.Headers["Authorization"] = " Bearer " + itsToken;
//OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = requestMessage;
//return request;
WebProxy wproxy = new WebProxy(new Uri("our proxy server and port"), true);
wproxy.BypassProxyOnLocal = true;
wproxy.UseDefaultCredentials = true;
WebRequest.DefaultWebProxy = wproxy;
//// Add a HTTP Header to an outgoing request
HttpRequestMessageProperty hrmp = request.Properties["httpRequest"] as HttpRequestMessageProperty;
hrmp.Headers["Authorization"] = " Bearer " + itsToken;
return request;
}
public void AfterReceiveReply(ref Message reply, object correlationState)
{
//Console.WriteLine("Received the following reply: '{0}'", reply.ToString());
}
#endregion
}
public class AuthenticationHeaderBehavior : IEndpointBehavior
{
#region Implementation of IEndpointBehavior
readonly string itsUser;
readonly string itsPass;
readonly string itsToken;
public AuthenticationHeaderBehavior(string user, string pass, string token)
: base()
{
itsUser = user;
itsPass = pass;
itsToken = token;
}
public void Validate(ServiceEndpoint endpoint) { }
public void AddBindingParameters(ServiceEndpoint endpoint,
BindingParameterCollection bindingParameters) { }
public void ApplyDispatchBehavior(ServiceEndpoint endpoint,
EndpointDispatcher endpointDispatcher) { }
public void ApplyClientBehavior(ServiceEndpoint endpoint,
ClientRuntime clientRuntime)
{
clientRuntime.MessageInspectors.Add(new AuthenticationHeader(itsUser, itsPass, itsToken));
}
#endregion
}
}
I think the answer is that I forgot the binding for my channel.

WCF service hosted in azure worker role returns 400 (Bad Request) error

I have hosted wcf service in worker role on http port and i iopen that port onStart method as given bellow in code
public override bool OnStart()
{
try
{
ServicePointManager.DefaultConnectionLimit = 12;
CreateServiceHost();
return base.OnStart();
}
catch (Exception)
{
return true;
}
// Set the maximum number of concurrent connections
}
private ServiceHost serviceHost;
private void CreateServiceHost()
{
//serviceHost = new ServiceHost(typeof(Service));
//WebHttpBinding binding = new WebHttpBinding(WebHttpSecurityMode.None);
//RoleInstanceEndpoint externalEndPoint =
// RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["ServiceEP"];
//string endpoint = String.Format("http://{0}/Service",
// externalEndPoint.IPEndpoint);
//serviceHost.AddServiceEndpoint(typeof(IService), binding, endpoint);
//serviceHost.Open();
var endpoint = RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["ServiceEP"];
var ub = new UriBuilder();
ub.Host = endpoint.IPEndpoint.Address.ToString();
ub.Port = endpoint.IPEndpoint.Port;
ub.Scheme = "http";
ub.Path = "Service";
var host = new ServiceHost(typeof(Service), ub.Uri);
host.AddDefaultEndpoints();
host.Description.Behaviors.Add(new ServiceMetadataBehavior() { HttpGetEnabled = true });
foreach (var e in host.Description.Endpoints)
{
((BasicHttpBinding)e.Binding).HostNameComparisonMode = HostNameComparisonMode.Exact;
}
host.Open();
//Trace.WriteLine("WCF Hello service running...");
}
when i try to access method defined in service i got bellow error
The remote server returned an error: (400) Bad Request.

Private key is not present in x509

Why am I getting this exception. The private key is not present in x509 certificate
This is to generate a username with nonce+ 2 binary security tokens(one for the usercertificate and one for the server).
My user certificate has a private key but the server certificate does not have one.
This is for Asymmetric binding.
using System;
using System.IdentityModel.Selectors;
using System.IdentityModel.Tokens;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Security;
using System.Text;
namespace MYEmedny
{
public class CustomCredentials : ClientCredentials
{
private X509Certificate2 clientAuthCert;
private X509Certificate2 clientSigningCert;
public CustomCredentials() : base() { }
public CustomCredentials(CustomCredentials other)
: base(other)
{
// other.clientSigningCert = SetCertificate(StoreLocation.CurrentUser, StoreName.My, X509FindType.FindBySubjectName, "LMWARD");
clientSigningCert = other.clientSigningCert;
clientAuthCert = other.clientAuthCert;
}
protected override ClientCredentials CloneCore()
{
CustomCredentials scc = new CustomCredentials(this);
return scc;
}
public CustomCredentials(X509Certificate2 ClientAuthCert, X509Certificate2 ClientSigningCert)
: base()
{
clientAuthCert = ClientAuthCert;
clientSigningCert = ClientSigningCert;
}
public X509Certificate2 ClientAuthCert
{
get { return clientAuthCert; }
set { clientAuthCert = value; }
}
public X509Certificate2 ClientSigningCert
{
get { return clientSigningCert; }
set { clientSigningCert = value; }
}
public override SecurityTokenManager CreateSecurityTokenManager()
{
return new CustomTokenManager(this);
}
}
public class CustomTokenManager : ClientCredentialsSecurityTokenManager
{
private CustomCredentials custCreds;
public CustomTokenManager(CustomCredentials CustCreds)
: base(CustCreds)
{
custCreds = CustCreds;
}
public override SecurityTokenProvider CreateSecurityTokenProvider(SecurityTokenRequirement tokenRequirement)
{
try
{
if (tokenRequirement.TokenType == SecurityTokenTypes.X509Certificate)
{
x509CustomSecurityTokenProvider prov;
object temp = null;
//TransportSecurityBindingElement secBE = null;
AsymmetricSecurityBindingElement secBE = null;
if (tokenRequirement.Properties.TryGetValue("http://schemas.microsoft.com/ws/2006/05/servicemodel/securitytokenrequirement/SecurityBindingElement", out temp))
{
//secBE = (TransportSecurityBindingElement)temp;
secBE = (AsymmetricSecurityBindingElement)temp;
}
if (secBE == null)
prov = new x509CustomSecurityTokenProvider(custCreds.ClientAuthCert);
else
prov = new x509CustomSecurityTokenProvider(custCreds.ClientSigningCert);
return prov;
}
}
catch (Exception ex)
{
throw ex;
}
return base.CreateSecurityTokenProvider(tokenRequirement);
}
public override System.IdentityModel.Selectors.SecurityTokenSerializer CreateSecurityTokenSerializer(System.IdentityModel.Selectors.SecurityTokenVersion version)
{
return new CustomTokenSerializer(System.ServiceModel.Security.SecurityVersion.WSSecurity10);
}
}
class x509CustomSecurityTokenProvider : SecurityTokenProvider
{
private X509Certificate2 clientCert;
public x509CustomSecurityTokenProvider(X509Certificate2 cert)
: base()
{
clientCert = cert;
}
protected override SecurityToken GetTokenCore(TimeSpan timeout)
{
return new X509SecurityToken(clientCert);
}
}
public class CustomTokenSerializer : WSSecurityTokenSerializer
{
public CustomTokenSerializer(SecurityVersion sv) : base(sv) { }
protected override void WriteTokenCore(System.Xml.XmlWriter writer, System.IdentityModel.Tokens.SecurityToken token)
{
if (writer == null)
{
throw new ArgumentNullException("writer");
}
if (token == null)
{
throw new ArgumentNullException("token");
}
if (token.GetType() == new UserNameSecurityToken("x", "y").GetType())
{
UserNameSecurityToken userToken = token as UserNameSecurityToken;
if (userToken == null)
{
throw new ArgumentNullException("userToken: " + token.ToString());
}
string tokennamespace = "wsse";
DateTime created = DateTime.Now;
string createdStr = created.ToString("yyyy-MM-ddThh:mm:ss.fffZ");
string phrase = Guid.NewGuid().ToString();
string nonce = GetSHA1String(phrase);
string password = userToken.Password;
//string password = userToken.Password;
writer.WriteStartElement(tokennamespace, "UsernameToken", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
writer.WriteAttributeString("wsu", "Id", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd", token.Id);
writer.WriteElementString(tokennamespace, "Username", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", userToken.UserName);
writer.WriteStartElement(tokennamespace, "Password", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
writer.WriteAttributeString("Type", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText");
writer.WriteValue(password);
writer.WriteEndElement();
writer.WriteStartElement(tokennamespace, "Nonce", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
writer.WriteAttributeString("EncodingType", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary");
writer.WriteValue(nonce);
writer.WriteEndElement();
writer.WriteElementString(tokennamespace, "Created", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", createdStr);
writer.WriteEndElement();
writer.Flush();
}
else
{
base.WriteTokenCore(writer, token);
}
}
protected string GetSHA1String(string phrase)
{
SHA1CryptoServiceProvider sha1Hasher = new SHA1CryptoServiceProvider();
byte[] hashedDataBytes = sha1Hasher.ComputeHash(Encoding.UTF8.GetBytes(phrase));
return Convert.ToBase64String(hashedDataBytes);
}
}//CustomTokenSerializer
}
This is how I am using it
private CustomBinding GetCustomBinding()
{
AsymmetricSecurityBindingElement secBE = (AsymmetricSecurityBindingElement)SecurityBindingElement.CreateMutualCertificateBindingElement
(
MessageSecurityVersion.WSSecurity10WSTrust13WSSecureConversation13WSSecurityPolicy12BasicSecurityProfile10
);
secBE.ProtectTokens = false;
X509SecurityTokenParameters x509ProtectionParameters = new X509SecurityTokenParameters();
x509ProtectionParameters.RequireDerivedKeys = false;
x509ProtectionParameters.X509ReferenceStyle = X509KeyIdentifierClauseType.SubjectKeyIdentifier;
x509ProtectionParameters.ReferenceStyle = SecurityTokenReferenceStyle.Internal;
//x509ProtectionParameters.InclusionMode = SecurityTokenInclusionMode.AlwaysToRecipient;
secBE.MessageSecurityVersion = System.ServiceModel.MessageSecurityVersion.WSSecurity11WSTrust13WSSecureConversation13WSSecurityPolicy12;
secBE.InitiatorTokenParameters = x509ProtectionParameters;
secBE.RecipientTokenParameters = x509ProtectionParameters;
secBE.MessageProtectionOrder = System.ServiceModel.Security.MessageProtectionOrder.SignBeforeEncrypt;
secBE.SecurityHeaderLayout = SecurityHeaderLayout.Strict;
secBE.EnableUnsecuredResponse = true;
secBE.SetKeyDerivation(false);
secBE.DefaultAlgorithmSuite = System.ServiceModel.Security.SecurityAlgorithmSuite.TripleDesRsa15;
secBE.EndpointSupportingTokenParameters.Signed.Add(new UserNameSecurityTokenParameters());
secBE.ProtectTokens = true;
// secBE.in = SecurityTokenInclusionMode.Never, RequireDerivedKeys = false );
secBE.EnableUnsecuredResponse = true;
secBE.IncludeTimestamp = false;
TextMessageEncodingBindingElement textEncBE = new TextMessageEncodingBindingElement(MessageVersion.Soap11WSAddressing10, System.Text.Encoding.UTF8);
HttpsTransportBindingElement httpsBE = new HttpsTransportBindingElement();
httpsBE.RequireClientCertificate = true;
CustomBinding myBinding = new CustomBinding();
myBinding.Elements.Add(secBE);
myBinding.Elements.Add(textEncBE);
myBinding.Elements.Add(httpsBE);
return myBinding;
}
ProxyGeneration.MHSClient proxy = new ProxyGeneration.MHSClient(GetCustomBinding(), new EndpointAddress(new Uri("https://service100.emedny.org:9047/MHService"), EndpointIdentity.CreateDnsIdentity("DPMedsHistory"), new AddressHeaderCollection()));
var vs = proxy.Endpoint.Behaviors.Where((i) => i.GetType().Namespace.Contains("VisualStudio"));
proxy.Endpoint.Behaviors.Remove((System.ServiceModel.Description.IEndpointBehavior)vs.Single());
proxy.ClientCredentials.ClientCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindBySubjectName,"User");
proxy.ClientCredentials.ServiceCertificate.SetDefaultCertificate(StoreLocation.LocalMachine, StoreName.TrustedPeople, X509FindType.FindBySubjectName, "Server");
proxy.Endpoint.Behaviors.Remove(typeof(ClientCredentials));
proxy.Endpoint.Behaviors.Add(new CustomCredentials(GetCertificateFromStore("User"), GetCertificateFromStore("Server")));
proxy.ClientCredentials.UserName.UserName = "User1";
proxy.ClientCredentials.UserName.Password = "PWD";

My test indicates .NET Remoting is faster than WCF 4 by a factor of 1.5

In order to tell whether my project should migrate from .net remoting to WCF, I extracted its network communication part and implemented it by WCF. I run the remoting version and wcf version and eventually find remoting is faster than wcf by a factor of 1.5, which greatly differs from the msdn article.
Test configuration
WCF and .NET Remoting both use tcp channel without encryption, no app.config file. Compiled in release mode, no optimization.
Operations
What my program does is this.
You can download the two solutions here.
WCF test
Service Host
ServiceHost host = new ServiceHost(typeof(Server), new Uri(string.Format("net.tcp://{0}:{1}/{2}", args[0], args[1], args[2])));
var binding = new NetTcpBinding();
binding.MaxReceivedMessageSize = 614400;
binding.ReaderQuotas.MaxArrayLength = 512000;//a max picture of 500KB
binding.Security.Mode = SecurityMode.None;
host.AddServiceEndpoint(typeof(IServer), binding, string.Empty);
host.Open();
Server
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single
//, IncludeExceptionDetailInFaults = true
, ConcurrencyMode = ConcurrencyMode.Reentrant
)]
public class Server : IServer
{
public EntryRequirement GetEntryRequirement()
{
return new EntryRequirement(new[] { "fuck", "sex" }, false);
}
public void AddClient()
{
var client = OperationContext.Current.GetCallbackChannel<IServerCallback>();
var p = client.Profile;
var x = client.Password;
System.Diagnostics.Debug.WriteLine(p);
System.Diagnostics.Debug.WriteLine(x);
}
}
Client side
Player player = new Player();
player.Password = "12423";
player.Profile = new Contracts.PlayerProfile
{
Description = "I'm a man.",
HeadImage = imageData,
Name = "Loveright"
};
var binding = new NetTcpBinding();
binding.Security.Mode = SecurityMode.None;
Stopwatch watch = new Stopwatch();
watch.Start();
for (int i = 0; i < 20; i++)
{
ServerProxy server = new ServerProxy(player, binding,
new EndpointAddress(string.Format("net.tcp://{0}:{1}/{2}", args[0], args[1], args[2])));
server.GetEntryRequirement();
server.AddClient();
}
watch.Stop();
HeadImage is a picture of size 139KB.
Player class
[CallbackBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant)]
class Player : IServerCallback
{
public PlayerProfile Profile { get; set; }
public string Password { get; set; }
public void ClientCollectionChangedEventHandler(object sender, ControllersChangedEventArgs e)
{
}
public void ClientUpdatedEventHandler(object sender, ClientUpdatedEventArgs e)
{
}
}
.NET Remoting test
Host
var serverProv = new BinaryServerFormatterSinkProvider();
serverProv.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;
var clientProv = new BinaryClientFormatterSinkProvider();
IDictionary props = new Hashtable();
props["port"] = args[1];
props["name"] = "tcp server";
var channel = new TcpChannel(props, clientProv, serverProv);
ChannelServices.RegisterChannel(channel, false);
System.Runtime.Remoting.RemotingConfiguration.RegisterWellKnownServiceType(typeof(Server),
args[2], System.Runtime.Remoting.WellKnownObjectMode.Singleton);
Client
var serverProv = new BinaryServerFormatterSinkProvider();
serverProv.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;
var clientProv = new BinaryClientFormatterSinkProvider();
IDictionary props = new Hashtable();
props["name"] = "tcp client " + Guid.NewGuid();
props["port"] = 0;
var channel = new TcpChannel(props, clientProv, serverProv);
ChannelServices.RegisterChannel(channel, false);
FileStream stream = new FileStream(#"logotz6.png", FileMode.Open);
byte[] imageData = new byte[stream.Length];
stream.Read(imageData, 0, imageData.Length);
stream.Close();
Player player = new Player();
player.Password = "12423";
player.Profile = new PlayerProfile
{
Description = "I'm a man.",
HeadImage = imageData,
Name = "Loveright"
};
Stopwatch watch = new Stopwatch();
watch.Start();
for (int i = 0; i < 20; i++)
{
var serverProxy = (IServer)Activator.GetObject(typeof(IServer), string.Format("tcp://{0}:{1}/{2}", args[0], args[1], args[2]));
serverProxy.GetEntryRequirement();
serverProxy.AddClient(player);
}
watch.Stop();
You can download the two solutions here.
Result
So do I make the test somewhere unfair to WCF?
Should it be the message encoding king ?
Have you used binaryMessageEncoding instead of textMessageEncoding or soapMessageEncoding ?
You can create a custom binding to do this :
internal sealed class MyBinding : CustomBinding
{
private static readonly BindingElementCollection elementCollection;
static MyBinding()
{
MessageEncodingBindingElement encoding = new BinaryMessageEncodingBindingElement();
TcpTransportBindingElement transport = new TcpTransportBindingElement();
elementCollection = new BindingElementCollection();
elementCollection.Add(encoding);
elementCollection.Add(transport);
}
internal MyBinding(string bindingName, string bindingNamespace)
: base()
{
base.Namespace = bindingNamespace;
base.Name = bindingName;
}
public override BindingElementCollection CreateBindingElements()
{
return elementCollection;
}
}