WCF Encryption/Decrpytion using both Server and Client certificates - c#

I have created a service where there would be a private cert on the server where i am hosting the service and the client will have public key of it.
And the client would have a different private key where they will encrypt the message which they send to the endpoint i create and i have the public key for it which i will use to decrypt the message.
What i have so far in the server config file.
So this one takes care of the main private cert where the service will be hosted. I am not sure where/how to put the public key of the cert where client has/uses the private key to encrypt the message.
Any help would be really appreciated.
<?xml version="1.0"?>
<configuration>
<appSettings>
</appSettings>
<system.web>
<httpRuntime maxRequestLength="2147483647"/>
<compilation debug="false" strict="false" explicit="true" targetFramework="4.5.2"/>
<pages controlRenderingCompatibilityVersion="4.0"/>
<customErrors mode="Off"/>
</system.web>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="basicHttpEndPointBinding">
<security mode="Message">
<message clientCredentialType="Certificate"/>
</security>
</binding>
</basicHttpBinding>
</bindings>
<services>
<service behaviorConfiguration="wcfJNet.ServiceBehavior" name="wcfJNetService">
<endpoint address="" binding="basicHttpBinding"
bindingConfiguration="basicHttpEndPointBinding"
contract="IJNetService">
<identity>
<dns value="xxxxxx" />
</identity>
</endpoint>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="wcfJNet.ServiceBehavior">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
<serviceCredentials>
<serviceCertificate findValue="0000xx000" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySerialNumber"/>
<clientCertificate>
<authentication certificateValidationMode="PeerOrChainTrust"/>
</clientCertificate>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<protocolMapping>
<add binding="basicHttpsBinding" scheme="https"/>
</protocolMapping>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
<!--
To browse web app root directory during debugging, set the value below to true.
Set to false before deployment to avoid disclosing web app folder information.
-->
<directoryBrowse enabled="true"/>
</system.webServer>
</configuration>

Very good, you have a deep understanding of the working mechanism of the SSL certificate. Please refer to the below link.
https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/message-security-with-a-certificate-client
The client-side and server-side automatically negotiate the public key of the certificates during communication to encrypt the message with the other's public key and decrypt the soap message using the private key. Thereby we don’t need to manually program this procedure. It is enough to install each other’s certificate in the local certificate store.
If we authenticate the client with message security mode, we need to use the service credential section to configure the service certificate. Just like what you have done.
<serviceCredentials>
<serviceCertificate findValue="0000xx000" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySerialNumber"/>
<clientCertificate>
<authentication certificateValidationMode="PeerOrChainTrust"/>
</clientCertificate>
</serviceCredentials>
On the client-side, generally, we need to specify two certificates, one is service certificate, another is client certificate.
//message security, we need to specify both the default certificate and the client certificate.
ServiceReference1.ServiceClient client = new ServiceReference1.ServiceClient(); client.ClientCredentials.ServiceCertificate.SetDefaultCertificate(StoreLocation.LocalMachine, StoreName.Root, X509FindType.FindByThumbprint, "cbc81f77ed01a9784a12483030ccd497f01be71c");
client.ClientCredentials.ClientCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindByThumbprint, "9b8db0dfe615458ace0ae9e89fcb983c5d16f633");
try
{
var result = client.SayHello();
Console.WriteLine(result);
}
catch (Exception)
{
throw;
}
As for the trust relationship between the certificates, on the client-side,we need to install the server certificate in the LocalCA, and on the server-side, we need to install the client certificate in the particular location depending on the authenticating mode. By default it is ok to install it in the LocalCA.
//this is default authentication mode.
sh.Credentials.ClientCertificate.Authentication.CertificateValidationMode= System.ServiceModel.Security.X509CertificateValidationMode.ChainTrust;
Feel free to let me know if there is anything I can help with.

Related

How to add user/pass authentication in WCF

I want to add some security to my WCF application service. I figured out how to add username/password authentication:
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="MyBehavior">
<serviceMetadata httpGetEnabled="false" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="Changer.Service.Validation.ServiceAuthenticator, Changer.Service"/>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<wsHttpBinding>
<binding name="MyBinding">
<security mode="TransportWithMessageCredential">
<message clientCredentialType="UserName" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<services>
<service name="Changer.Service.Request.RequestService" behaviorConfiguration="MyBehavior">
<endpoint address="/" binding="wsHttpBinding" contract="Changer.Service.Request.IRequestService" bindingConfiguration="MyBinding" />
</service>
</services>
</system.serviceModel>
This is my custom data validation:
public class ServiceAuthenticator : UserNamePasswordValidator
{
public override void Validate(string userName, string password)
{
// Check the user name and password
if (userName != Authentication.Providers.Service.PasswordChanger.UserName ||
password != Authentication.Providers.Service.PasswordChanger.Password)
{
throw new System.IdentityModel.Tokens.SecurityTokenException("Unknown username or password.");
}
}
}
Unfortunelly I am getting error which is because I have no valid certificate. I tried following this tutorial:
https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/how-to-configure-an-iis-hosted-wcf-service-with-ssl
But without success. It says, that certificate hosts is not matching site url I am visiting. On client side I am getting error:
Could not establish trust relationship for the SSL/TLS secure channel with authority 'foo'. The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel. The remote certificate is invalid according to the validation procedure
I can solve this by adding to my client app:
System.Net.ServicePointManager.ServerCertificateValidationCallback += delegate { return true; };
Which is basically not solving my problem.. What can I do with that? I just want to have simple user/pass authentication.
I decided to get rid off SSL, then my code changed to:
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="MyBehavior">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="false" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="PasswordChanger.Service.Validation.ServiceAuthenticator, PasswordChanger.Service"/>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<wsHttpBinding>
<binding name ="NewBinding">
<security mode="Message">
<message clientCredentialType="UserName"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
<protocolMapping>
<add binding="basicHttpsBinding" scheme="https" />
</protocolMapping>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
</system.serviceModel>
And after that I got this error:
Error: Cannot obtain Metadata from http://localhost:53705/R.svc If this is a Windows (R) Communication Foundation service to which you have access, please check that you have enabled metadata publishing at the specified address. For help enabling metadata publishing, please refer to the MSDN documentation at www.WS-Metadata Exchange Error URI: http://localhost:53705/R.svc Metadata contains a reference that cannot be resolved: 'http://localhost:53705/R.svc'. Content Type application/soap+xml; charset=utf-8 was not supported by service http://localhost:53705/R.svc. The client and service bindings may be mismatched. The remote server returned an error: (415) Cannot process the message because the content type 'application/soap+xml; charset=utf-8' was not the expected type 'text/xml; charset=utf-8'..HTTP GET Error URI: http://localhost:53705/R.svc The HTML document does not contain Web service discovery information.
So I decided to add services tag to my web.config next to bindings
<services>
<service name="PasswordChanger.Service.Request.RequestService" behaviorConfiguration="MyBehavior">
<endpoint address="/" binding="wsHttpBinding" contract="PasswordChanger.Service.Request.IRequestService" bindingConfiguration="NewBinding" />
</service>
And I got another error:
The service certificate is not provided. Specify a service certificate in ServiceCredentials.
Whether we use the message security or transport layer security mode, we all need to provide a certificate to ensure that the username/password authentication mode is secure.
I have made an example related transport security mode. we need provide a certificate to ensure that the service is hosted successfully.
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
<serviceCredentials>
<userNameAuthentication customUserNamePasswordValidatorType="WcfService1.CustUserNamePasswordVal,WcfService1" userNamePasswordValidationMode="Custom"/>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<wsHttpBinding>
<binding>
<security mode="TransportWithMessageCredential">
<message clientCredentialType="UserName"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
<protocolMapping>
<add binding="wsHttpBinding" scheme="https" />
</protocolMapping>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
</system.serviceModel>
If we don’t have a certificate, we could generate a self-signed certificate by using IIS Server Certificate Tool.
then we add https binding in the IIS website binding module so that the WCF service is hosted successfully.
Custom Authentication class. Based on the actual situation, configure this authentication class in the configuration file above.
internal class CustUserNamePasswordVal : UserNamePasswordValidator
{
public override void Validate(string userName, string password)
{
if (userName != "jack" || password != "123456")
{
throw new Exception("Username/Password is not correct");
}
}
}
<serviceCredentials>
<userNameAuthentication customUserNamePasswordValidatorType="WcfService1.CustUserNamePasswordVal,WcfService1" userNamePasswordValidationMode="Custom"/>
</serviceCredentials>
Client.
//for validating the server certificate.
ServicePointManager.ServerCertificateValidationCallback += delegate
{
return true;
};
ServiceReference2.Service1Client client = new ServiceReference2.Service1Client();
client.ClientCredentials.UserName.UserName = "jack";
client.ClientCredentials.UserName.Password = "123456";
if we use the message security, we could set up the certificate by the following code.( Configure your actual certificate according to the actual situation)
<serviceCredentials>
<serviceCertificate storeLocation="LocalMachine" storeName="My" x509FindType="FindByThumbprint" findValue="869f82bd848519ff8f35cbb6b667b34274c8dcfe"/>
<userNameAuthentication customUserNamePasswordValidatorType="WcfService1.CustUserNamePasswordVal,WcfService1" userNamePasswordValidationMode="Custom"/>
</serviceCredentials>
Refer to the below link.
WCF-TransportWithMessageCredential The HTTP request is unauthorized with client authentication scheme 'Anonymous'
WCF UserName & Password validation using wshttpbinding notworking
Feel free to let me know if there is anything I can help with.

WCF UserName & Password validation using wshttpbinding notworking

I am new to WCF Service authentication, I was trying to achieve wcfauthentication using wshttpbinding. but i am getting below exception.
Could not find a base address that matches scheme https for the endpoint with binding WSHttpBinding. Registered base address schemes are [http].
Web.Config:
<?xml version="1.0"?>
<configuration>
<appSettings>
<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
</appSettings>
<system.web>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5"/>
</system.web>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="wsHttp">
<security mode="TransportWithMessageCredential">
<message clientCredentialType="UserName"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
<services>
<service name="WCFAuth.Service1" behaviorConfiguration="wsHttpBehavior">
<endpoint address="" binding="wsHttpBinding" bindingConfiguration="wsHttp" contract="WCFAuth.IService1">
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
<host>
<baseAddresses>
<add baseAddress="http://localhost:64765/"/>
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="wsHttpBehavior">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="WCFAuth.ServiceAuthanticator, WCFAuth"/>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
<!--
To browse web app root directory during debugging, set the value below to true.
Set to false before deployment to avoid disclosing web app folder information.
-->
<directoryBrowse enabled="true"/>
</system.webServer>
</configuration>
Service Authentication class:
using System;
using System.Collections.Generic;
using System.IdentityModel.Selectors;
using System.Linq;
using System.ServiceModel;
using System.Web;
namespace WCFAuth
{
public class ServiceAuthanticator : UserNamePasswordValidator
{
public override void Validate(string userName, string password)
{
string AppUserName = "ABC";
string AppPwd = "abc";
try
{
if (userName.ToLower() != AppUserName.ToLower() && password != AppPwd)
{
throw new FaultException("Unknown Username or Incorrect Password");
}
}
catch (Exception ex)
{
throw new FaultException("Unknown Username or Incorrect Password");
}
}
}
}
Client Side config file:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<!--<binding name="base" />-->
<binding name="base">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Basic"/>
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:64765/Service1.svc" binding="basicHttpBinding"
bindingConfiguration="base" contract="WCFAuth.IService1" name="base" />
</client>
</system.serviceModel>
</configuration>
Consumer:
class Program
{
static void Main(string[] args)
{
try
{
WCFAuth.Service1Client client = new WCFAuth.Service1Client();
client.ClientCredentials.UserName.UserName = "test";
client.ClientCredentials.UserName.Password = "test";
var temp = client.GetData(1);
Console.WriteLine(temp);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadKey();
}
}
I am getting attached exception when i try to browser svc file.
Can someone correct me, where i am committing mistake, thanks in advance.
The problem here is that you are using a WSHttpBinding with Transport Security, but the base address you set is http. It is not possible to work with http here, because you are sending credentials over the wire.
Either change it to https, or create a second binding configuration for development purposes. One with Transport Security (https), and a second without (http).
Also make sure that your clients binding matches the binding from your server.
As Marc mentioned, we are supposed to provide a certificate when hosting the service. there might be something amiss during the process of hosting the service.
Here is a reference configuration, wish it is useful to you.
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="wsHttp">
<security mode="TransportWithMessageCredential">
<message clientCredentialType="UserName"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
<services>
<service name="WCFAuth.Service1" behaviorConfiguration="wsHttpBehavior">
<endpoint address="" binding="wsHttpBinding" bindingConfiguration="wsHttp" contract="WCFAuth.IService1">
</endpoint>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="wsHttpBehavior">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="WCFAuth.ServiceAuthanticator, WCFAuth"/>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
Then we should add a https binding in IIS Site Bindings module.
The service address would be https://x.x.x.x:8865/Service1.svc
One thing must be noted that we should trust the service certificate when we call the service by adding service reference.
ServicePointManager.ServerCertificateValidationCallback += delegate
{
return true;
};
ServiceReference2.Service1Client client = new ServiceReference2.Service1Client();
client.ClientCredentials.UserName.UserName = "jack";
client.ClientCredentials.UserName.Password = "123456";
Besides, if we use SecurityMode.Message, we are supposed to provide a certificate in code snippets.
<serviceCredentials>
<serviceCertificate storeLocation="LocalMachine" storeName="My" x509FindType="FindByThumbprint" findValue="869f82bd848519ff8f35cbb6b667b34274c8dcfe"/>
<userNameAuthentication customUserNamePasswordValidatorType="WcfService1.CustUserNamePasswordVal,WcfService1" userNamePasswordValidationMode="Custom"/>
</serviceCredentials>
Feel free to let me know if there is anything I can help with.

WCF WF Service, Could not establish secure channel for SSL/TLS with authority

I keep getting the same error when trying to access my WCF WF Service: "Could not establish secure channel for SSL/TLS with authority."
I have tried a bunch of stackoverflow/google solutions but no luck so far.
I've made a WCF WF Service that connects to a thirdparty that uses a certificate to verify. My Web.config looks like this:
<?xml version="1.0"?>
<configuration>
<appSettings>
<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true"/>
</appSettings>
<system.web>
<compilation debug="true" strict="false" explicit="true"
targetFramework="4.5.1"/>
<httpRuntime targetFramework="4.5.1"/>
</system.web>
<system.serviceModel>
<bindings>
<basicHttpsBinding>
<binding name="binding1">
<security mode="TransportWithMessageCredential">
<message clientCredentialType="Certificate" />
</security>
</binding>
</basicHttpsBinding>
</bindings>
<client>
<!-- Test Endpoint -->
<endpoint address="https://example.com"
behaviorConfiguration="endpointBehavior"
binding="basicHttpsBinding"
bindingConfiguration="binding1"
contract="ContractPortType"
name="Port">
</endpoint>
</client>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="endpointBehavior">
<clientCredentials>
<clientCertificate findValue="SOMETHUMBPRINT"
storeLocation="LocalMachine"
storeName="My"
x509FindType="FindByThumbprint" />
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
<protocolMapping>
<add binding="basicHttpsBinding" scheme="https"/>
</protocolMapping>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"
multipleSiteBindingsEnabled="true"
minFreeMemoryPercentageToActivateService="1"/>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
The client is just a simple console running this:
using (var client = new ServiceClient())
{
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
var msg = client.GetData();
Console.WriteLine(msg);
}
That has the following App.config:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
</startup>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IService"/>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:55670/GetData.xamlx"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IService"
contract="Reference.IService" name="BasicHttpBinding_IService" behaviorConfiguration="endpointBehavior" />
</client>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="endpointBehavior">
<clientCredentials>
<clientCertificate findValue="SOMETHUMBPRINT"
storeLocation="LocalMachine"
storeName="My"
x509FindType="FindByThumbprint" />
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
It would be great if somebody could help me out.
EDIT:
So the thing that I can't seem to get working is the SSL connection needed between my WCF WF serivce and the thirdparty service. When I directly call the thirdparty service from a client then I can set the System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
but I can't find a way to do the same for my WCF WF service.
EDIT:
So I can make it work by creating an activity and then calling that activity before I call the thirdparty activity and then it works, but there has to be a better way!
public sealed class SetTlsVersion : CodeActivity
{
protected override void Execute(CodeActivityContext context)
{
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
}
}
I ran into a similar issue earlier this year and it turns out that some SSL certificates utilize TLS 1.2.
.NET 4.5 defaults the TLS version to v1.1. This can be changed in your code by the following snippet:
using System.Net;
// ...
// In your application startup flow, set the security protocol to TLS 1.2.
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
The above should be invoked at application startup time ideally. After that, I think you'll be good to go.
Side note: .NET 4.0 targeted applications will need a slightly different trick to set the TLS version:
ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072;
And in .NET 3.5 and lower, TLS 1.2 is not even supported unfortunately. But in your case it looks like your targeting .NET 4.5 so your good to go on this front.
EDIT: I noticed in your client code snippet that you bit-wise OR'd the TLS versions, which is incorrect. It should be a single value, SecurityProtocolType.Tls12 in this case.
I'm assuming the underlying .NET Framework networking code was only using SecurityProtocolType.Tls in the end, which doesn't match what the SSL / TLS certificate supports.
Hopefully that helps.
Well the only way that I was able to do this sort of nice was with a HttpModule (thanks ajawad987 for the tip). I'm still convinced that this could (or should) be done in the Web.config only.
public class InitializerModule : IHttpModule
{
public void Dispose() { }
public void Init(HttpApplication context)
{
// Sets the TLS version for every request made to an external party
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
}
}
Web.config
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<add name="InitializerModule" type="WorkflowService.Helpers.InitializerModule"/>
</modules>
</system.webServer>

HTTP Error 401.2 - Unauthorized You are not authorized to view this page due to invalid authentication headers.(For basic Authentication)

I want to enable "Basic" Authentication to my service. Disabled anonymous and windows Auth. And enabled "basicAuthentication" in applicationhost.config, but im not able access the service. Getting below error.
HTTP Error 401.2 - Unauthorized You are not authorized to view this page due to invalid authentication headers.
I am using IIS Express 10. Here is my applicationhost.config
<authentication>
<anonymousAuthentication enabled="false" userName="" />
<basicAuthentication enabled="true" />
<clientCertificateMappingAuthentication enabled="false" />
<digestAuthentication enabled="false" />
<iisClientCertificateMappingAuthentication enabled="false">
</iisClientCertificateMappingAuthentication>
<windowsAuthentication enabled="false">
<providers>
<add value="Negotiate" />
<add value="NTLM" />
</providers>
</windowsAuthentication>
</authentication>
And here is my Web.config
<?xml version="1.0"?>
<!--
For more information on how to configure your ASP.NET application, please visit
http://go.microsoft.com/fwlink/?LinkId=169433
-->
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.5.2">
<assemblies>
<add assembly="System.Net.Http, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
</assemblies>
</compilation>
<httpRuntime targetFramework="4.5.2"/>
</system.web>
<!--Enable directory browsing in the Server-->
<system.webServer>
<directoryBrowse enabled="true"/>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
<system.serviceModel>
<bindings>
<webHttpBinding>
<binding name="webHttpTransportSecurity">
<security mode="Transport">
<transport clientCredentialType="Basic"></transport>
</security>
</binding>
</webHttpBinding>
</bindings>
<services>
<service name="MyWCFServices.DemoREST" behaviorConfiguration="DemoREST">
<!--Define base https base address-->
<host>
<baseAddresses>
<add baseAddress="https://localhost:44300/HelloWorldService.svc/"/>
</baseAddresses>
</host>
<!--webHttpBinding allows exposing service methods in a RESTful manner-->
<endpoint name="rest" address="" binding="webHttpBinding"
contract="MyWCFServices.IDemoREST" bindingConfiguration="webHttpTransportSecurity"
behaviorConfiguration="DemoREST"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="DemoREST">
<!-- To avoid disclosing metadata information, set the value below to
false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="false" httpsGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true.
Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false"/>
<!--Using custom username and Password-->
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="MyWCFServices.CustomUserNameValidator, HelloWorldService" />
</serviceCredentials>
<serviceAuthenticationManager authenticationSchemes="Basic"/>
</behavior>
</serviceBehaviors>
<!--Required default endpoint behavior when using webHttpBinding-->
<endpointBehaviors>
<behavior name="DemoREST">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
And Custom validator class:
namespace MyWCFServices
{
class CustomUserNameValidator : UserNamePasswordValidator
{
// This method validates users. It allows in two users, user1 and user2
// This code is for illustration purposes only and
// must not be used in a production environment because it is not secure.
public override void Validate(string userName, string password)
{
if (null == userName || null == password)
{
throw new ArgumentNullException("You must provide both the username and password to access this service");
}
if (!(userName == "user1" && password == "test") && !(userName == "user2" && password == "test"))
{
// This throws an informative fault to the client.
throw new FaultException("Unknown Username or Incorrect Password");
// When you do not want to throw an informative fault to the client,
// throw the following exception.
// throw new SecurityTokenException("Unknown Username or Incorrect Password");
}
}
}
}
Please guide me if I am wrong anywhere.

How do I programatically assign WCF ClientCredentials ServiceCertificate property?

I have a WCF service being hosted over https with a self-signed certificate. I'm having trouble programatically creating the binding: specifically the portion of the endpoint behavior.
My Service config looks like this:
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="DisableServiceCertificateValidation">
<clientCredentials>
<serviceCertificate>
<authentication certificateValidationMode="None"
revocationMode="NoCheck" />
</serviceCertificate>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<customBinding>
<binding name="ContactEmail.Web.EmailService.customBinding0">
<binaryMessageEncoding />
<httpsTransport/>
</binding>
</customBinding>
</bindings>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
<services>
<service name="ContactEmail.Web.EmailService">
<endpoint address="https://xxxxxxxxxxxxxx/EmailService/EmailService.svc" binding="customBinding" bindingConfiguration="ContactEmail.Web.EmailService.customBinding0" contract="ContactEmail.Web.EmailService" behaviorConfiguration="DisableServiceCertificateValidation" />
<endpoint address="mex" binding="mexHttpsBinding" contract="IMetadataExchange" />
</service>
</services>
</system.serviceModel>
And when I use the "Add Service Reference" feature, the generated client works as expected. Given that I call set up a Cert Validation Callback like this:
System.Net.ServicePointManager.ServerCertificateValidationCallback = ((sender, certificate, chain, sslPolicyErrors) => true);
However, rather than feed the service configuration through a config file on the client, I need to set it programmatically, because the call will be part of a commonly shared library. So, I'm trying to do this by providing my own parameters to the client constructor like:
var myClient = new EmailServiceClient(GetBinding(), new EndpointAddress(Strings.EmailServiceEndpointAddress));
In GetBinding(), I create CustomBinding with BindingElements like HttpsTransportBindingElement, BinaryMessageEncodingBindingElement and SecurityBindingElement.CreateSecureConversationBindingElement(SecurityBindingElement.CreateUserNameOverTransportBindingElement()).
Do you know how I can specify things like certificateValidationMode="None" and revocationMode="NoCheck" or if I'm doing anything wrong?
SecureConversation is implementation of WS-SecureConversation => advanced message level security where special security token is created during first call to the service (authenticated by the message security mode passed as parameter to the binding element creation) and this token is used to secure subsequent messages. This security also forms something know as security context or security session.
Your current binding in config file is not using SecureConversation so your binding defined in code is not compatible with your service.
You should have a Credentials property (of type ClientCredentials) (http://msdn.microsoft.com/en-us/library/ms733836.aspx) on your ClientBase (EmailServiceClient)...and that should have a ServiceCertificate property:
http://msdn.microsoft.com/en-us/library/system.servicemodel.description.clientcredentials.servicecertificate.aspx

Categories