Custom WCF client certificate over ssl validation - wcf

Hello,
I am trying to use WCF to do some authentication:
Authenticate the user using Username/Passoword
Authenticate the client using a client certificate
Customize which root certificate are accepted
After some trial and error, i managed to get points 1 & 2 working, but i am stuck on 3. This is my service configuration
<system.serviceModel>
<behaviors>
<endpointBehaviors />
<serviceBehaviors>
<behavior name="MyBehavior">
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom"
customUserNamePasswordValidatorType="WcfService1.CustomValidator, WcfService1" />
</serviceCredentials>
<serviceMetadata httpsGetEnabled="true" httpGetEnabled="false" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<customBinding>
<binding name="certificate">
<security authenticationMode="UserNameOverTransport" />
<textMessageEncoding messageVersion="Soap12WSAddressing10" />
<httpsTransport requireClientCertificate="true" />
</binding>
</customBinding>
</bindings>
<services>
<service behaviorConfiguration="MyBehavior" name="WcfService1.Service1">
<endpoint address="" binding="customBinding" bindingConfiguration="certificate"
contract="WcfService1.IService1" />
</service>
</services>
</system.serviceModel>
and this is my client configuration
<client>
<endpoint name="service1" address="https://localhost:443/WcfService1/Service1.svc" binding="customBinding"
bindingConfiguration="certificate" behaviorConfiguration="certificate" contract="WcfService1.IService1" />
</client>
<behaviors>
<endpointBehaviors>
<behavior name="certificate">
<clientCredentials>
<clientCertificate storeName="My" storeLocation="LocalMachine" x509FindType="FindBySubjectName"
findValue="SignedByCA" />
</clientCredentials>
</behavior>
</endpointBehaviors>
<serviceBehaviors />
</behaviors>
<bindings>
<customBinding>
<binding name="certificate">
<security authenticationMode="UserNameOverTransport" />
<textMessageEncoding messageVersion="Soap12WSAddressing10" />
<httpsTransport requireClientCertificate="true" />
</binding>
</customBinding>
</bindings>
Using the client and attaching username credentials works nicely
var channelFactory = new ChannelFactory<IService1>("service1");
var user = channelFactory.Credentials.UserName;
user.UserName = username;
user.Password = password;
Using OperationContext.Current.ServiceSecurityContext.AuthorizationContext.ClaimSets gives me access to the username and the name and thumbprint of the certificate. Sadly, i am unable to find the IssuerName of the certificate. How else can I disallow clients that do not have a certificate issued by a certain root certificate?
Any hints to point me into the right direction or any alternatives are greatly welcome ;)
Thanks

Actually, it's easy, but a hack. There is a list of identities in the authorization context.
OperationContext.Current.ServiceSecurityContext
.AuthorizationContext.Properties["Identities"]
One of which is of type X509Identity. That one is internal to System.IdentityModel, you you can't get it directly.
identity.GetType().Name == "X509Identity"
It doesn't really matter, because the field containing the certificate is private, anyways :)
var field = identity.GetType().GetField(
"certificate",
BindingFlags.GetField | BindingFlags.Instance | BindingFlags.NonPublic);
var certificate = (X509Certificate2) field.GetValue(identity);
string issuer = certificate.Issuer;

Related

Expose WCF Endpoints with REST and SOAP using authentication with simplified configuration

As usual, trying to find good examples to do exactly what I want is proving difficult.
I've written a WCF Service in .NET 4.5 with simplified configuration, so I've got no services section in the configuration file.
I want to provide SOAP and REST APIs. I also need to restrict access by username and password and I've written a class derived from UserNamePasswordValidator that handles this.
By importing the service reference into a desktop app I can call the service using this code:
var svc = new MyUserService.UserServiceClient();
svc.ClientCredentials.UserName.UserName = "TestApp";
svc.ClientCredentials.UserName.Password = "******";
I expect to be able to call the service from Javascript using something like this:
$.ajax({
url: 'https://domain.userservice.svc',
type: 'GET',
contentType: "application/json",
success: function(data){
},
data: requestData,
headers: {
User: "TestApp",
Password: "********"
}
});
Here's the config file so far, with the SOAP service working.
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="wsHttpBindingConfig">
<security mode="TransportWithMessageCredential">
<message clientCredentialType="UserName" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="MyAuthenticationModule.Security.WcfUsernameValidator, MyAuthenticationModule.Security" />
<serviceCertificate findValue="TestSelfSigned" storeLocation="LocalMachine" x509FindType="FindBySubjectName" storeName="My" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<protocolMapping>
<add scheme="http" binding="wsHttpBinding" bindingConfiguration="wsHttpBindingConfig" />
</protocolMapping>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
</system.serviceModel>
By blindly following examples from around the web I ended up with this:
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="wsHttpBindingConfig">
<security mode="TransportWithMessageCredential">
<message clientCredentialType="UserName" />
</security>
</binding>
</wsHttpBinding>
<webHttpBinding>
<binding name="webHttpBindingJson">
<security mode="None" />
</binding>
</webHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="MyAuthenticationModule.Security.WcfUsernameValidator, MyAuthenticationModule.Security" />
<serviceCertificate findValue="TestSelfSigned" storeLocation="LocalMachine" x509FindType="FindBySubjectName" storeName="My" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="jsonEndpointBehaviour">
<enableWebScript/>
</behavior>
<behavior name="poxEndpointBehaviour">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
<protocolMapping>
<add scheme="http" binding="wsHttpBinding" bindingConfiguration="wsHttpBindingConfig" />
<add scheme="httpJson" binding="webHttpBinding" bindingConfiguration="webHttpBindingJson"/>
</protocolMapping>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
</system.serviceModel>
I don't think that I'm too far from it, but I know I'm not there. Can someone finish of the configuration, or point me in the right direction? What url will I need to call from JavaScript?
If I were to add a Web API to support the REST service then I could handle the cross origin issues by catching the request in Global.asax. How will I handle cors in the WFC service.
And a bonus question... I wanted to test the username on my dev machine / password authentication without having to install a certificate and go to https, and apply https later on the deployed site, but this doesn't look possible. Am I correct?
Thanks for helping...

The remote server returned an error: (401) Unauthorized - WCF basicHttpBinding

I get this error when I want to execute a method on my service with wcfstorm client.
I tried so many different settings and I can't get it to work. If I switch to Transport security I get another error.. It worked perfectly with wsHttpBinding but customer wants basicHttpBinding because of PHP compatibility. I need a custom user authentication class to authenticate users against my sql database..
web.config
<services>
<service name="e3kConnector.E3KConnectorService" behaviorConfiguration="basicHttpBehavior">
<endpoint address="" binding="basicHttpBinding"
bindingConfiguration="basicHttpBinding" name="basicHttpEndpoint"
contract="e3kConnector.e3kConnectorService" />
</service>
</services>
<bindings>
<basicHttpBinding>
<binding name="basicHttpBinding">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Basic"/>
</security>
</binding>
</basicHttpBinding>
</bindings>
<client />
<behaviors>
<serviceBehaviors>
<behavior name="basicHttpBehavior">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom"
customUserNamePasswordValidatorType="e3kConnector.App_Data.Security.CustomUserNameValidator,e3kConnector" />
</serviceCredentials>
<serviceAuthorization principalPermissionMode="Custom">
<authorizationPolicies>
<add policyType="e3kConnector.App_Data.Security.AuthorizationPolicy, e3kConnector" />
</authorizationPolicies>
</serviceAuthorization>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />

WCF - The HTTP request was forbidden with client authentication scheme 'Anonymous'

I know this is a very common scenario, but I've still, after two days of searching, not found a solution to this problem.
I've got a WCF Service and a client (web site), using SSL and a client certificate.
Relevant service config section:
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="HOLBinding">
<security mode="Transport">
<transport clientCredentialType="Certificate" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="HOLServiceBehaviour">
<serviceCredentials>
<clientCertificate>
<authentication certificateValidationMode="PeerOrChainTrust" trustedStoreLocation="LocalMachine" />
</clientCertificate>
</serviceCredentials>
<serviceMetadata httpGetEnabled="false" httpsGetEnabled="false" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
<services>
<service name="HOL.Core.Service.HOLService" behaviorConfiguration="HOLServiceBehaviour">
<endpoint address="bh" bindingConfiguration="HOLBinding" binding="basicHttpBinding" contract="HOL.Core.Service.IHOLService" />
<endpoint address="wb" behaviorConfiguration="WebBehaviour" binding="webHttpBinding" contract="HOL.Core.Service.IHOLService" />
</service>
</services>
Relevant client service config:
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="HOLServiceEndpointBehaviour">
<clientCredentials>
<clientCertificate storeLocation="LocalMachine"
findValue="mythumbprint"
x509FindType="FindByThumbprint" storeName="TrustedPeople" />
<serviceCertificate>
<authentication certificateValidationMode="PeerOrChainTrust"/>
</serviceCertificate>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IHOLService" maxBufferPoolSize="20000000" maxReceivedMessageSize="20000000">
<security mode="Transport">
<transport clientCredentialType="Certificate" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="https://www.myhttpsite.co.uk/Service/HOLService.svc/bh" binding="basicHttpBinding"
bindingConfiguration="BasicHttpBinding_IHOLService" contract="HOLCoreService.IHOLService"
name="BasicHttpBinding_IHOLService" behaviorConfiguration="HOLServiceEndpointBehaviour">
</endpoint>
</client>
</system.serviceModel>
My certificate is being found, so that's not the problem (took me a day to fix that problem too!)
I believe the error is that the client calling the WCF Service simply isn't sending through the correct details to authenticate... but why?

Https binding in WCF throws error

I have a WCF service that needs to hosted using basicHttpBinding using SSL.
So my team has installed a SSL certificate with Anonymous authentication enabled and a hardcoded username and password given in IIS.
I tried giving this binding
<binding name="SecurityByTransport">
<security mode="Transport">
<transport clientCredentialType="Windows" />
</security>
</binding>
But it still doesnt work. I get this error "Could not find a base address that matches scheme http for the endpoint with binding BasicHttpBinding. Registered base address schemes are [https]. "
ServiceModel section:
<system.serviceModel>
<diagnostics>
<messageLogging logEntireMessage="true" logMalformedMessages="true" logMessagesAtTransportLevel="true" />
</diagnostics>
<bindings>
<customBinding>
<binding name="netTcp">
<binaryMessageEncoding>
<readerQuotas maxArrayLength="2147483647" maxStringContentLength="2147483647" maxDepth="64" />
</binaryMessageEncoding>
<security authenticationMode="UserNameOverTransport" />
<windowsStreamSecurity />
<tcpTransport portSharingEnabled="true" maxBufferPoolSize="52428800" maxReceivedMessageSize="2147483647" maxBufferSize="2147483647" />
</binding>
</customBinding>
<basicHttpBinding>
<binding name="Https">
<security mode="Transport">
</security>
</binding>
</basicHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name ="Https">
<serviceMetadata httpsGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="Asi.Soa.ServiceModelEx.NullUserNamePasswordValidator, Asi.Soa.ServiceModelEx" />
<clientCertificate>
<authentication certificateValidationMode="None" />
</clientCertificate>
</serviceCredentials>
<serviceAuthorization principalPermissionMode="Custom">
<authorizationPolicies>
<add policyType="Asi.Soa.ServiceModelEx.ClaimsAuthorizationPolicy, Asi.Soa.ServiceModelEx" />
</authorizationPolicies>
</serviceAuthorization>
<exceptionShielding/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="SoapBehavior">
</behavior>
<behavior name="RestBehavior">
</behavior>
<behavior name="AjaxBehavior">
<enableWebScript />
</behavior>
</endpointBehaviors>
</behaviors>
<services>
<service name="Asi.Soa.Core.Services.EntityService" >
<endpoint address="https://10.42.150.122/imis15/EntityService.svc" binding="basicHttpBinding" contract="Asi.Soa.Core.ServiceContracts.IEntityService"
bindingConfiguration="Https" />
</service>
</services>
Any help would be appreciated.
Thanks
Try to add to service behavior setting
<serviceMetadata httpsGetEnabled="true"/>
Also you should check that endpoind has a setting
bindingConfiguration="SecurityByTransport"
If you are hosting your service in IIS, please try to remove the http binding from the site bindings.
HTH
Since you are using the web service on IIS, use HTTPS binding for it considering that you are using SSL certificate.

How to use SSL with a WCF web service?

I have a web service in asp.net running and everything works fine. Now I need to access some methods in that web-service using SSL. It works perfect when I contact the web-service using http:// but with https:// I get "There was no endpoint listening at https://...".
Can you please help me on how to set up my web.config to support both http and https access to my web service. I have tried to follow guidelines but I can't get it working.
Some code:
My TestService.svc:
[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class TestService
{
[OperationContract]
[WebGet(ResponseFormat = WebMessageFormat.Json)]
public bool validUser(string email) {
return true;
}
}
My Web.config:
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
<behaviors>
<endpointBehaviors>
<behavior name="ServiceAspNetAjaxBehavior">
<enableWebScript />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="ServiceBehavior">
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
<behavior name="">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="ServiceBehavior" name="TestService">
<endpoint address="" behaviorConfiguration="ServiceAspNetAjaxBehavior"
binding="webHttpBinding" bindingConfiguration="ServiceBinding"
contract="TestService" />
</service>
</services>
<bindings>
<webHttpBinding>
<binding name="ServiceBinding" maxBufferPoolSize="1000000" maxReceivedMessageSize="1000000">
<readerQuotas maxDepth="1000000" maxStringContentLength="1000000" maxArrayLength="1000000" maxBytesPerRead="1000000" maxNameTableCharCount="1000000"/>
</binding>
</webHttpBinding>
</bindings>
</system.serviceModel>
Instead of using the webHttpBinding, try creating a customBinding instead:
<customBinding>
<binding name="poxBindingHttps" closeTimeout="00:00:20">
<textMessageEncoding writeEncoding="utf-8" />
<httpsTransport manualAddressing="true"/>
</binding>
<binding name="poxBindingHttp" closeTimeout="00:00:20">
<textMessageEncoding writeEncoding="utf-8" />
<httpTransport manualAddressing="true"/>
</binding>
</customBinding>
You'll also need to setup the webHttpBehavior like so:
<behaviors>
<endpointBehaviors>
<behavior name="ajaxBehavior">
<enableWebScript/>
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
And finally, your service & endpoint:
<services>
<service name="TestService">
<endpoint name="sslDefault" address="" binding="customBinding" bindingConfiguration="poxBindingHttps" behaviorConfiguration="ajaxBehavior" contract="TestService"/>
<endpoint name="noSslDefault" address="" binding="customBinding" bindingConfiguration="poxBindingHttp" behaviorConfiguration="ajaxBehavior" contract="TestService"/>
</service>
</services>
Hopefully that works out for you in creating an SSL endpoint
It sounds like there is more an issue with IIS no listening on port 443 (The port used for HTTPS). Check IIS that there is a certificate installed and that it is listening on the secure port.