I've created a custom membership provider and I am trying to use it to secure my WCF service. But, I am getting this error:
The service certificate is not provided. Specify a service certificate in ServiceCredentials.
I don't want to use an x509 certificate. How can I get this working?
Here is my service config:
<?xml version="1.0"?>
<bindings>
<wsHttpBinding>
<binding name="wsHttpBinding1" maxBufferPoolSize="2147483647"
maxReceivedMessageSize="2147483647">
<readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647"
maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
<security mode="Message">
<message clientCredentialType="UserName"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
<services>
<service name="UpdateService.UpdateService" behaviorConfiguration="ASPNETProviders">
<endpoint address="" binding="wsHttpBinding" bindingConfiguration="wsHttpBinding1"
contract="UpdateService.IUpdateService" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="ASPNETProviders">
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="MembershipProvider" membershipProviderName="myUpdateMembershipProvider"/>
</serviceCredentials>
</behavior>
<behavior>
<!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="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" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"
multipleSiteBindingsEnabled="true" />
UPDATE
This blog post, enter link description here, says:
[When] you opt in for the UserName client credential type, WCF insists that your service must also reference a service certificate that contains a private key.
I am going to host my service in IIS and plan to use SSL certificate in the web site for encrypting communication. Can I make WCF not insist that I reference a service certificate?
It seems that Microsoft really, really wants me to use a certificate.
Someone developed a Clear Username Binding for my situation, but I think I may just surrender and use a certificate.
Have you try to create a custom credential validator ? See this : http://nayyeri.net/custom-username-and-password-authentication-in-wcf-3-5
Related
I'm a beginner with WCF services. Trying to implement a Certificated based authentication on a WCF service and facing an issue. The service expects a specific Certificate from the calling client. The server throws an authentication error if the client is not passing any certificate. But at the same time, the service call is passing authentication with any certificates provided by the client(The service suppose to authenticate if the client provides a specific certificate).
Following is the code snippet of server config :
Service Config :
<bindings>
<wsHttpBinding>
<binding name="MyWsHttpBinding" maxReceivedMessageSize="2147483647" receiveTimeout="00:30:00">
<readerQuotas maxStringContentLength="2147483647" maxBytesPerRead="2147483647" maxDepth="2147483647" maxArrayLength="2147483647"/>
<security mode="Transport">
<transport clientCredentialType="None" proxyCredentialType="None"/>
<message clientCredentialType="Certificate" algorithmSuite="Default"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
<serviceBehaviors>
<behavior name="MyServiceBehavior">
<serviceCredentials>
<clientCertificate>
<authentication certificateValidationMode="ChainTrust" />
</clientCertificate>
<serviceCertificate findValue="e616ebcd940951794736624acc6484802018c8d4" storeLocation="LocalMachine" storeName="My" x509FindType="FindByThumbprint" />
</serviceCredentials>
<serviceMetadata httpsGetEnabled="true" httpGetEnabled="true"/>
<CustomBehaviorExtensionElement/>
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="MyEndpointBehavior">
<MySchemaValidator validateRequest="True" validateReply="False">
<schemas>
<add location="App_Data\model-service.xsd"/>
</schemas>
</MySchemaValidator>
</behavior>
</endpointBehaviors>
<services>
<service name="MyService" behaviorConfiguration="MyServiceBehavior">
<endpoint binding="wsHttpBinding" bindingConfiguration="MyWsHttpBinding" contract="MyExchangeService" behaviorConfiguration="MyEndpointBehavior" bindingNamespace="http://www.mycompany.com/exchange/"/>
<endpoint contract="IMetadataExchange" binding="mexHttpsBinding" address="mex" name="mex"/>
</service>
</services>
The cause of the problem is the security mode you use is transport, so only the following code works:
<transport clientCredentialType="None" proxyCredentialType="None"/>
The following message settings have no effect:
<message clientCredentialType="Certificate" algorithmSuite="Default"/>
Change the value in transport to certificate, you can also download the wcf demo on the official website, there are examples of related certificate verification, and there are tutorials corresponding to the demo.
I see that the certificate validation mode used in your code is ChainTrust.
<clientCertificate>
<authentication certificateValidationMode="ChainTrust" />
</clientCertificate>
As mentioned in Microsoft Docs, using ChainTrust means -
The certificate is valid if the chain builds to a certification authority in the trusted root store
Meaning, the client need not send certificate with the exact same thumbprint as mentioned in your service web.config. Infact, any certificate whose Root / Intermediate Certification Authority is present in your VM's Trusted Root Store will pass validation.
To make sure that the client is able to use only a specific certificate to authenticate to your service, change ChainTrust to PeerTrust and add the certificate to the trusted people store on your VM's Certificate Store (certmgr).
<authentication certificateValidationMode="PeerTrust" />
References:
MS Docs - Working with certificates in WCF
Authentication element in web.config
More info on Certificate Chain of Trust
I have been reading the WCF configuration documentation and would like a confirmation on a limitation that I've encountered.
I have a client that expects the following binding configuration:
<bindings>
<basicHttpBinding>
<binding name="secureHttpBinding">
<security mode="Transport">
<transport clientCredentialType="Basic"/>
</security>
</binding>
</basicHttpBinding>
</bindings>
On the server side I have implemented a custom user name and password validator because we don't use Windows or domain accounts for clients. I followed this guide: https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/how-to-use-a-custom-user-name-and-password-validator, but they and other examples I've seen use wsHttpBinding or add message security.
The server endpoints and behaviors look something like this:
<services>
<service name="MyWcfService.Service" behaviorConfiguration="MyBehavior">
<endpoint
address=""
binding="basicHttpBinding"
bindingConfiguration="secureHttpBinding"
contract="MyWcfService.IService"/>
<endpoint
address="mex"
binding="mexHttpsBinding"
contract="IMetadataExchange"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="MyBehavior">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
<serviceCredentials>
<userNameAuthentication
userNamePasswordValidationMode="Custom"
customUserNamePasswordValidatorType="MyValidator, MyWcfService"/>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
When hosting the service in IIS with basic authentication enabled the custom validator is not hit, IIS tries to validate the provided credentials against Windows accounts and fails with this message if the user account doesn't exist:
The HTTP request is unauthorized with client authentication scheme 'Basic'.
So is it impossible to have a custom credential validator for a service hosted in IIS with basicHttpBinding, Transport security mode, and Basic transport client credential type? Is there no way to override the basic Windows authentication that IIS does?
We should use UserName authentication instead of the Basic authentication, subsequently, we could have a custom credential validator for that service.
<basicHttpBinding>
<binding name="mybinding">
<security mode="TransportWithMessageCredential">
<message clientCredentialType="UserName"/>
</security>
</binding>
</basicHttpBinding>
<serviceCredentials>
<userNameAuthentication customUserNamePasswordValidatorType="WcfService3.CustUserNamePasswordVal,WcfService3" userNamePasswordValidationMode="Custom"/>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
For details,
How to add user/pass authentication in WCF
Feel free to let me know if there is anything I can help with.
So far it looks like it's not possible to use a custom credential validator with this binding configuration.
<basicHttpBinding>
<binding name="secureHttpBinding">
<security mode="Transport">
<transport clientCredentialType="Basic"/>
</security>
</binding>
I have gone through this post and answer in depth: https://stackoverflow.com/a/20667308/463196
and am still getting the 413 Request Entity Too Large Error from the WCF Client (consumer of the Service Reference).
I've increased the maxXYZSize for everything, and my binding name "TransportSecurity" is bound to the endpoint correctly.
From Web.config of Service (Provider):
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="TransportSecurity" maxBufferPoolSize="2147483647" maxBufferSize="2147483647" maxReceivedMessageSize="2147483647" messageEncoding="Text" closeTimeout="10:00:00" openTimeout="10:00:00" receiveTimeout="10:00:00" sendTimeout="10:00:00">
<readerQuotas maxDepth="2000000" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647"/>
</binding>
</basicHttpBinding>
</bindings>
<services>
<service behaviorConfiguration="" name="GeneratePdf_CloudService.IService1">
<endpoint address="" binding="basicHttpBinding" bindingConfiguration="TransportSecurity" contract="GeneratePdf_CloudService.IService1" listenUri="">
<identity>
<dns value=""/>
</identity>
</endpoint>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information, set the value below to false before deployment -->
<serviceMetadata httpGetEnabled="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"/>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true"/>
</system.serviceModel>
Does anyone see anything I am missing?
Thanks!
EDIT: Oops. Some more relevant information ... I am doing an Azure WebJob as the WCF Client, should have mentioned that.
So for all intents and purposes, it is a Client App that uses AppName.exe.config. I am going to go and provide information on that end and see if it helps.
I have a WCF service which uses such behaviors:
<behavior name="WCFServiceCertificate.Service1Behavior">
<!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="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"/>
<serviceCredentials>
<clientCertificate>
<authentication certificateValidationMode="ChainTrust"/>
</clientCertificate>
<serviceCertificate findValue="localhost" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName"/>
</serviceCredentials>
</behavior>
It uses a certficate named "localhost" which I created by makecert. First I created Root Certification Authority and then a certificate. I generated also a client certificate which was saved in a file.
Then, I have a client application which consumes that webservice. App.config consists of:
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_IWS">
<security>
<message clientCredentialType="Certificate" />
</security>
</binding>
</wsHttpBinding>
</bindings>
And I load the cerificate for client from a file:
X509Certificate2 client = new X509Certificate2("client.pfx", "pass");
All certificates' stuff seem to be done properly, but when I want to invoke any service method from the client, it says:
The caller was not authenticated by the service
Could someone, please, give me some advice on how to pass the certificate in SOAP header properly from the client to server? What do I miss?
Working with certificate is not an easy task specially for debugging it in WCF. For this reason you may consider the following steps to narrow down your problem.
Does your Service run after you embed certificate? One way to test if it is working properly is to try hitting your service in your browser.
Ex. http://localhost/Path/Service.svc
Are you accessing the service address in your client same as what is describe in CN=localhost certificate?
Ex. http://location/Path/Service.svc
And not this :
Ex. http://computername/Path/Service.svc
Or
Ex. http://computername.domain.com/Path/Service.svc
Explanation :
Certificate will not work if the name in the Common Name of certificate is different in the URL name issued to that certificate.
Are you in the same machine? Certificate with CN=localhost will only work in same machine.
Since you host your Service in IIS you need to configure your IIS to use the certificate you created and bind it in port 443.
How?
Go to you IIS and Client Default Web Site
Click Binding location at the right side
If protocol HTTPS is already configure try to edit it and assign your certificate. If not, Add the protocol HTTPS with port 443 and assign your certificate. Certificate should visible in the list if you installed properly in the store.
Through these steps we may able to know where to start. It could be at service problem, client problem or IIS configuration. Hope this will help us both to solve your problem.
I am struggling with the issue all the time. I reconfigured both client and the server side.
Web.config (server):
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="wsHttpEndpointBinding">
<security>
<message clientCredentialType="Certificate" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<services>
<service name="WSInfo.WS" behaviorConfiguration="WCFServiceCertificate.Service1Behavior">
<!-- Service Endpoints -->
<endpoint address="" binding="wsHttpBinding" bindingConfiguration="wsHttpEndpointBinding" contract="WSInfo.IWS">
<!--
Upon deployment, the following identity element should be removed or replaced to reflect the
identity under which the deployed service runs. If removed, WCF will infer an appropriate identity
automatically.
-->
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="WCFServiceCertificate.Service1Behavior">
<!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="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"/>
<serviceCredentials>
<clientCertificate>
<authentication certificateValidationMode="ChainTrust"/>
</clientCertificate>
<serviceCertificate findValue="ForServer"
storeLocation="LocalMachine"
storeName="My"
x509FindType="FindBySubjectName" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
And app.config (WinForms Client App):
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_IService1" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text"
textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<reliableSession ordered="true" inactivityTimeout="00:10:00"
enabled="false" />
<security mode="Message">
<transport clientCredentialType="Windows" proxyCredentialType="None"
realm="" />
<message clientCredentialType="Certificate" negotiateServiceCredential="true"
algorithmSuite="Default" establishSecurityContext="true" />
</security>
</binding>
<binding name="WSHttpBinding_IWS">
<security>
<message clientCredentialType="Certificate" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="http://XX.XX.XXX.XXX:XX/WSInfo/WS.svc" behaviorConfiguration="CustomBehavior"
binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IService1"
contract="WSInfo.IWS" name="WSHttpBinding_IWS">
<identity>
<dns value="ForServer" />
</identity>
</endpoint>
</client>
<behaviors>
<endpointBehaviors>
<behavior name="CustomBehavior">
<clientCredentials>
<clientCertificate findValue="Client" x509FindType="FindBySubjectName" storeLocation="CurrentUser" storeName="My" />
<serviceCertificate>
<authentication certificateValidationMode="ChainTrust"/>
</serviceCertificate>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
I generated certificates in an application called XCA. First, on the server I generated the root cerificate and a certficate for client. I exported it and imported on client machine. Then I generated the root certificate on the client machine and the next certificate for server. I exported it and imported in server's system. I think the configuration files are OK, but maybe something's wrong with certificates - when I want to invoke a method from the client I get "The caller was not authenticated by the service". I tried to add HTTPS with my certificate but it resulted in problems with connection to IIS. Now IIS is off... I'll look up the solution but please verify if my configs are OK.
Our azure web application already uses https port 443 with our site certificate, we have a WCF service within this webrole that has an https endpoint using our cert to authenticate (1-way ssl), this same service needs an additional https endpoint supporting 2-way auth using our cert and the third party's cert. We have uploaded the cert, updated the service definition file, and added an endpoint that we are hoping will work, but in testing we are getting the error: The SSL settings for the service 'SslRequireCert' does not match those of the IIS 'None'.
so the endpoint that does work is: https://environemnt.application.com/Services/Service.svc
the endpoint that generates the error: https://environment.application.com/Services/Service.svc/twa
The key requirement is that it is https, port 443, at the above new endpoint, without altering the SSL behavior of the rest of the role, I have seen entries to change the IIS configuration or use the role editor to add an Https Input endpoint, but as we already have an Https Input endpoint on port 443 using our site cert I don't want to alter/affect the whole role.
If it is helpful the service is a WCF Service which consumes an Mtom encoded soap 1.2 message
here are the new values that we have entered, what else do I need?
<behaviors>
<serviceBehaviors>
<behavior name="SSLServiceBehavior">
<serviceMetadata httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<dataContractSerializer maxItemsInObjectGraph="2147483647" />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="OneWayAuthEndpointBehavior">
</behavior>
<behavior name="TwoWayAuthEndpointBehavior">
<endpointDiscovery enabled="true"></endpointDiscovery>
<clientCredentials>
<clientCertificate findValue="thumprint..." storeLocation="LocalMachine" storeName="CertificateAuthority" x509FindType="FindByThumbprint" />
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="SSLServiceBehavior" name="Service">
<endpoint address="" behaviorConfiguration="OneWayAuthEndpointBehavior"binding="wsHttpBinding" bindingConfiguration="HttpsMtomOneWay" contract="ITestService" />
<endpoint address="twa" behaviorConfiguration="TwoWayAuthEndpointBehavior" binding="wsHttpBinding" bindingConfiguration="HttpsMtomTwoWay" contract="ITestService"/>
</services>
<bindings>
<wsHttpBinding>
<binding name="HttpsMtomOneWay" messageEncoding="Mtom">
<security mode="Transport">
<transport clientCredentialType="None" />
</security>
</binding>
<binding name="HttpsMtomTwoWay" messageEncoding="Mtom">
<security mode="Transport">
<transport clientCredentialType="Certificate" />
</security>
</binding>
</wsHttpBinding>
</bindings>
thank you much
Fixed through these steps:
Added serviceCredentials.serviceCertificate (cert details for our cert) to the service behavior
Eliminated Endpoint behavior definitions
Changed the HttpsMtomTwoWay binding to securityMode=Message
Now the message handlers handle the authentication exchange and external cert validation, then pass on to the transport endpoint, and we did not need to mess with the site wide SSL or endpoint settings. Tested and verified with numerous 3rd parties.