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...
Related
I'm having truble with configuration around my WCF service and WS-Seciurity.
I don't have access to the client side, so far I'm trying to use SoapUI as a client with WS-A adressing, userName, Password and WSS Password Type 'PasswordDigest' options.
I use IIS and https with a simple certificate, .NET 4.7.
I've tried many versions, but without success. I just want to find simplest, working solution to read 'Seciurity' header from SoapUI/client request with WS-Seciurity PasswordDigest options enabled.
The current error with the current config file 'InvalidSecurity' 'An error occurred when verifying security for the message'
<system.serviceModel>
<protocolMapping>
<add scheme="https" binding="wsHttpBinding"/>
</protocolMapping>
<services>
<service name="SoapService" behaviorConfiguration="SoapServiceConf">
<!--<endpoint address="SoapService" binding="wsHttpBinding" contract="MPA.SoapService.References.ServiceReference.SentSOAP" /> -->
<endpoint address="" binding="wsHttpBinding" contract="Interfaces.ISoap" bindingConfiguration="wsHttpBind"/>
<endpoint contract="IMetadataExchange" binding="mexHttpsBinding" address="mex" />
</service>
</services>
<bindings>
<wsHttpBinding>
<binding maxReceivedMessageSize="10485760" name="wsHttpBind">
<security mode="TransportWithMessageCredential">
<transport clientCredentialType="Basic"/>
<message clientCredentialType="UserName" algorithmSuite="Default" establishSecurityContext="false" />
</security>
<reliableSession enabled="false" />
<readerQuotas maxArrayLength="10485760" maxDepth="1024" maxStringContentLength="10485760" />
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="SoapServiceConf">
<serviceCredentials>
<serviceCertificate findValue="soapservice"
storeName="My"
x509FindType="FindByIssuerName" />
</serviceCredentials>
<serviceMetadata httpGetEnabled="false" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="False" />
</behavior>
<behavior name="MyServiceTypeBehaviors" >
<!-- Add the following element to your service behavior configuration. -->
<serviceMetadata httpGetEnabled="false" httpsGetEnabled="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
</system.serviceModel>
Thanks.
I have developed WCF windows service using net tcp binding. Its working fine when wcf client and wcf service both are in domain (in two different system)
Getting error when both system are in work group not in domain
please suggest what configuration i need to change .
Error :System.ServiceModel.CommunicationException: The socket connection was aborted. This could be caused by an error processing your message or a receive timeout being exceeded by the remote host, or an underlying network resource issue. Local socket timeout was '00:00:58.9879193'. ---> System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host
Client Config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.serviceModel>
<client>
<endpoint kind="discoveryEndpoint" address="net.tcp://localhost:8005/Probe" binding="netTcpBinding" bindingConfiguration="RequestReplyNetTcpBinding">
</endpoint>
<endpoint binding="netTcpBinding" bindingConfiguration="RequestReplyNetTcpBinding" contract="Test2ServLib.IService1" behaviorConfiguration="LargeEndpointBehavior">
<identity>
<dns value="WCFServer" />
</identity>
<!--The behaviorConfiguration is required to enable WCF deserialization of large data sets -->
</endpoint>
</client>
<behaviors>
<serviceBehaviors>
<behavior name="announcementBehavior">
<!--The following behavior attribute is required to enable WCF serialization of large data sets -->
<dataContractSerializer maxItemsInObjectGraph="2147483647" />
<serviceDiscovery>
<announcementEndpoints>
<endpoint kind="announcementEndpoint" address="net.tcp://localhost:8005/Announcement" binding="netTcpBinding" bindingConfiguration="RequestReplyNetTcpBinding" />
</announcementEndpoints>
</serviceDiscovery>
<serviceThrottling maxConcurrentCalls="1500" maxConcurrentSessions="1500" maxConcurrentInstances="1500" />
<clientCredentials>
<clientCertificate findValue="WCFClient"
storeLocation="LocalMachine"
storeName="TrustedPeople"
x509FindType="FindBySubjectName" />
<serviceCertificate >
<authentication certificateValidationMode="PeerTrust" revocationMode="NoCheck"/>
</serviceCertificate>
</clientCredentials>
</behavior>
<behavior name="LargeEndpointBehavior">
<!--The behavior is required to enable WCF deserialization of large data sets -->
<dataContractSerializer maxItemsInObjectGraph="2147483647" />
<clientCredentials>
<clientCertificate findValue="WCFClient"
storeLocation="LocalMachine"
storeName="TrustedPeople"
x509FindType="FindBySubjectName" />
<serviceCertificate >
<authentication certificateValidationMode="PeerTrust" revocationMode="NoCheck"/>
</serviceCertificate>
</clientCredentials>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="disableEndpointDiscovery">
<endpointDiscovery enabled="false" />
<!--The behavior is required to enable WCF deserialization of large data sets -->
<dataContractSerializer maxItemsInObjectGraph="2147483647" />
<clientCredentials>
<clientCertificate findValue="WCFClient"
storeLocation="LocalMachine"
storeName="TrustedPeople"
x509FindType="FindBySubjectName" />
<serviceCertificate >
<authentication certificateValidationMode="PeerTrust" revocationMode="NoCheck"/>
</serviceCertificate>
</clientCredentials>
</behavior>
<behavior name="LargeEndpointBehavior">
<!--The behavior is required to enable WCF deserialization of large data sets -->
<dataContractSerializer maxItemsInObjectGraph="2147483647" />
<clientCredentials>
<clientCertificate findValue="WCFClient"
storeLocation="LocalMachine"
storeName="TrustedPeople"
x509FindType="FindBySubjectName" />
<serviceCertificate >
<authentication certificateValidationMode="PeerTrust" revocationMode="NoCheck"/>
</serviceCertificate>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<netTcpBinding>
<binding name="RequestReplyNetTcpBinding" receiveTimeout="05:00:00" openTimeout="00:00:59" closeTimeout="00:00:59" maxBufferPoolSize="524288" maxBufferSize="25000000" maxConnections="50" maxReceivedMessageSize="25000000" sendTimeout="00:05:00" listenBacklog="1500">
<reliableSession ordered="false" inactivityTimeout="00:01:00" enabled="true" />
<readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
<security>
<message clientCredentialType="Certificate"/>
</security>
</binding>
</netTcpBinding>
</bindings>
</system.serviceModel>
</configuration>
Service Config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="announcementBehavior">
<!--The following behavior attribute is required to enable WCF serialization of large data sets -->
<dataContractSerializer maxItemsInObjectGraph="2147483647"/>
<serviceDiscovery>
<announcementEndpoints>
<endpoint kind="announcementEndpoint"
address="net.tcp://localhost:8005/Announcement"
binding="netTcpBinding"
bindingConfiguration="RequestReplyNetTcpBinding"/>
</announcementEndpoints>
</serviceDiscovery>
<serviceThrottling
maxConcurrentCalls="1500"
maxConcurrentSessions="1500"
maxConcurrentInstances="1500"/>
<serviceCredentials>
<serviceCertificate findValue="WCFServer"
storeLocation="LocalMachine"
storeName="TrustedPeople"
x509FindType="FindBySubjectName" />
<clientCertificate>
<authentication certificateValidationMode="PeerTrust" trustedStoreLocation="LocalMachine" revocationMode="NoCheck" mapClientCertificateToWindowsAccount="false" />
</clientCertificate>
</serviceCredentials>
</behavior>
<endpointBehaviors>
<behavior name="disableEndpointDiscovery">
<endpointDiscovery enabled="false"/>
<!--The behavior is required to enable WCF deserialization of large data sets -->
<dataContractSerializer maxItemsInObjectGraph="2147483647"/>
<serviceCredentials>
<serviceCertificate findValue="WCFServer"
storeLocation="LocalMachine"
storeName="TrustedPeople"
x509FindType="FindBySubjectName" />
<clientCertificate>
<authentication certificateValidationMode="PeerTrust" trustedStoreLocation="LocalMachine" revocationMode="NoCheck" mapClientCertificateToWindowsAccount="false" />
</clientCertificate>
</serviceCredentials>
</behavior>
<behavior name="LargeEndpointBehavior">
<!--The behavior is required to enable WCF deserialization of large data sets -->
<dataContractSerializer maxItemsInObjectGraph="2147483647"/>
<serviceCredentials>
<serviceCertificate findValue="WCFServer"
storeLocation="LocalMachine"
storeName="TrustedPeople"
x509FindType="FindBySubjectName" />
<clientCertificate>
<authentication certificateValidationMode="PeerTrust" trustedStoreLocation="LocalMachine" revocationMode="NoCheck" mapClientCertificateToWindowsAccount="false" />
</clientCertificate>
</serviceCredentials>
</behavior>
</endpointBehaviors>
</behavior>
</serviceBehaviors>
<service name="Test2ServLib.IService1"
behaviorConfiguration="announcementBehavior">
<host>
<baseAddresses>
<add baseAddress="net.tcp://localhost:8006/Service1"/>
</baseAddresses>
</host>
<endpoint binding="netTcpBinding"
bindingConfiguration="RequestReplyNetTcpBinding"
contract="Test2ServLib.IService1"
behaviorConfiguration="LargeEndpointBehavior" />
<bindings>
<netTcpBinding>
<binding RequestReplyNetTcpBinding>
<security>
<message clientCredentialType="Certificate" />
</security>
</binding>
</netTcpBinding>
</bindings>
</system.serviceModel>
</configuration>
According to the information you provide, it is very likely that the error is caused by the large amount of transmitted data. NetTcpbing will have a higher transmission performance in the local area network. SO you have no errors in the same domain. The WCF default timeout period is 1 minute. If the data is not transmitted within a minute, an error will be reported. I suggest you extend the timeout period on the server side:
<bindings>
<netTcpBinding>
<binding openTimeout="00:10:00"
closeTimeout="00:10:00"
sendTimeout="00:10:00"
receiveTimeout="00:20:00">
</binding>
</netTcpBinding>
</bindings>
Besides, please enable the windows feature for supporting NetTCP protocol.
Feel free to let me know if the problem persists.
UPDATE
If the client and server are not on the same machine, the client needs to provide windows credentials, because nettcpbinding defaults to windows authentication:
ServiceReference1.CalculatorClient calculatorClient = new ServiceReference1.CalculatorClient();
calculatorClient.ClientCredentials.Windows.ClientCredential.UserName = "Administrator";
calculatorClient.ClientCredentials.Windows.ClientCredential.Password = "Password";
If this problem still exists, it is recommended to add a mex endpoint:
<endpoint address="mex"
binding="mexTcpBinding"
contract="IMetadataExchange"></endpoint>
Set the Mode value to Message:
<binding name="Binding">
<security mode="Message">
<message clientCredentialType="Certificate" />
</security>
</binding>
In your configuration file, I found that you did not set the value of Mode.
This is my App.config:
<?xml version="1.0"?>
<configuration>
<system.serviceModel>
<services>
<service name="Microsoft.Samples.X509CertificateValidator.CalculatorService" behaviorConfiguration="CalculatorServiceBehavior">
<!-- use host/baseAddresses to configure base address provided by host -->
<host>
<baseAddresses>
<add baseAddress="net.tcp://localhost:8001/servicemodelsamples/service"/>
</baseAddresses>
</host>
<!-- use base address specified above, provide one endpoint -->
<endpoint address="certificate" binding="netTcpBinding" bindingConfiguration="Binding" contract="Microsoft.Samples.X509CertificateValidator.ICalculator"/>
</service>
</services>
<bindings>
<netTcpBinding>
<!-- X509 certificate binding -->
<binding name="Binding">
<security mode="Message">
<message clientCredentialType="Certificate" />
</security>
</binding>
</netTcpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="CalculatorServiceBehavior">
<serviceDebug includeExceptionDetailInFaults="true"/>
<serviceMetadata/>
<serviceCredentials>
<!--
The serviceCredentials behavior allows one to specify authentication constraints on client certificates.
-->
<clientCertificate>
<authentication certificateValidationMode="None" revocationMode="NoCheck"/>
</clientCertificate>
<!--
The serviceCredentials behavior allows one to define a service certificate.
A service certificate is used by a client to authenticate the service and provide message protection.
This configuration references the "localhost" certificate installed during the setup instructions.
-->
<serviceCertificate findValue="localhost" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName"/>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup></configuration>
I have created my first WCF service and it works perfectly, in https in Postman.
Now, I moved on to work with the Service in my VB.NET website and I can't get it to work.
Here is the Web.config from the Service
<system.serviceModel>
<diagnostics>
<messageLogging logEntireMessage="true" logMalformedMessages="true" logMessagesAtServiceLevel="true" logMessagesAtTransportLevel="true" />
</diagnostics>
<behaviors>
<serviceBehaviors>
<behavior name="inculdeExceptionDetails">
<serviceDebug includeExceptionDetailInFaults="true " />
</behavior>
<behavior>
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior>
<webHttp />
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<webHttpBinding>
<binding name="https">
<security mode="Transport">
<transport clientCredentialType="None" />
</security>
</binding>
</webHttpBinding>
</bindings>
<protocolMapping>
<add binding="webHttpBinding" scheme="http" />
<add binding="webHttpBinding" scheme="https" bindingConfiguration="https" />
</protocolMapping>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
</system.serviceModel>
I have tried numerous things on the client. But the point I am at now, I can initialize the class, but when I go to call a Function on it, I get:
The provided URI scheme 'https' is invalid; expected 'http'.
Here is my current web.config:
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="basicHttpBinding_ICustomZendeskWrapper">
<security mode="None">
<transport clientCredentialType="None"></transport>
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="https://www.myDomain.info/Webservices/WCF/CustomZendeskWrapper.svc" binding="basicHttpBinding" bindingConfiguration="basicHttpBinding_ICustomZendeskWrapper" contract="CustomZendeskWrapper.ICustomZendeskWrapper" />
</client>
</system.serviceModel>
Can someone point me in the right direction?
Here is my corrected settings:
Service Web.Config:
<system.serviceModel>
<diagnostics>
<messageLogging logEntireMessage="true" logMalformedMessages="true" logMessagesAtServiceLevel="true" logMessagesAtTransportLevel="true" />
</diagnostics>
<behaviors>
<serviceBehaviors>
<behavior name="inculdeExceptionDetails">
<serviceDebug includeExceptionDetailInFaults="true " />
</behavior>
<behavior>
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior>
<webHttp />
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<webHttpBinding>
<binding name="https">
<security mode="Transport">
<transport clientCredentialType="None" />
</security>
</binding>
</webHttpBinding>
</bindings>
<protocolMapping>
<add binding="webHttpBinding" scheme="http" />
<add binding="webHttpBinding" scheme="https" bindingConfiguration="https" />
</protocolMapping>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
</system.serviceModel>
Client Web.Config:
<system.serviceModel>
<bindings>
<webHttpBinding>
<binding name="webHttpBinding_ICustomZendeskWrapper">
<security mode="Transport">
<transport clientCredentialType="None"></transport>
</security>
</binding>
</webHttpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="webhttp">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
<client>
<endpoint address="https://www.myDomain.info/Webservices/WCF/CustomZendeskWrapper.svc" binding="webHttpBinding" bindingConfiguration="webHttpBinding_ICustomZendeskWrapper" behaviorConfiguration="webhttp" contract="CustomZendeskWrapper.ICustomZendeskWrapper" />
</client>
</system.serviceModel>
First, your configuration file creates the service by using WebHttpBinding, hence your service works properly over http and https in REST style. we are supposed to use WebHttpBinding to call the service or send http request to the proper URL instead of using BasicHttpBinding.
Under this circumstance, if you want to call the service by adding the service reference, just like your configuration. I suggest you make the following changes.
Use WebHttpBinding instead of BasicHttpBinding in client’s
webconfig.
Add the appropriate attribute to the auto-generated operation. [WebGet], [WebInvoke]
Add the webhttp endpoint behavior to the client endpoint.
This could work, but I don’t think it is you want. As you know, we call the service succesfully only when the wcf binding type between the server and the client is consistent. Another solution for this case is we create the service by using BasicHttpBinding. It also works well over http and http.
Please refer to my following configuration.
Server end.
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
<protocolMapping>
<add binding="basicHttpBinding" scheme="http"/>
<add binding="basicHttpsBinding" scheme="https"/>
</protocolMapping>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
</system.serviceModel>
Client end
//use self-signed certificate
ServicePointManager.ServerCertificateValidationCallback += delegate
{
return true;
};
ServiceReference2.Service1Client client = new ServiceReference2.Service1Client("BasicHttpsBinding_IService1");
var result=client.GetData(234);
Console.WriteLine(result);
Configuration file.
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IService1" />
<binding name="BasicHttpsBinding_IService1">
<security mode="Transport" />
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://vabqia593vm:11024/Service1.svc" binding="basicHttpBinding"
bindingConfiguration="BasicHttpBinding_IService1" contract="ServiceReference2.IService1"
name="BasicHttpBinding_IService1" />
<endpoint address="https://localhost:11025/Service1.svc" binding="basicHttpBinding"
bindingConfiguration="BasicHttpsBinding_IService1" contract="ServiceReference2.IService1"
name="BasicHttpsBinding_IService1" />
</client>
</system.serviceModel>
In addition, we are supposed to add http binding and https binding in IIS binding module.
Feel free to let me know if there is anything I can help with.
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" />
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;