Securing WCF REST service for use with iPhone application - iphone

I've a created a simple WCF REST service which I intend to consume from an iPhone application.
The service works fine but now I'd like to secure it.
In my test enviornment (IIS on Windows 7) I already setup a self signed certificate using makecert.exe.
I also overridden the validate() method so I can use a custom username & password (since windows authentication is out of the question).
Now I'm stuck for more than two days figuring out how to configure everything so it can work.
My goal now is to be able to do a simple GET request via the browser, something like:
https://localhost/testservice/service1.svc/sayHello
When this will work I'll continue on to all iPhone related stuff.
Any help / examples will be highly appreciated!
Here's my web.config:
<system.serviceModel>
<services>
<service name="IphoneWcf.Service1" behaviorConfiguration="IphoneWcf.Service1Behavior">
<!-- Service Endpoints -->
<endpoint address="" binding="basicHttpBinding" bindingConfiguration="webBinding" behaviorConfiguration="webBehavior" contract="IphoneWcf.IService1">
<!--
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"/> -->
<host>
<baseAddresses>
<add baseAddress="https://localhost/iphonewcf" />
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="webBehavior">
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="IphoneWcf.Service1Behavior">
<!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpsGetEnabled="false" />
<!-- 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="true" />
<serviceCredentials>
<serviceCertificate findValue="localhost" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<basicHttpBinding>
<binding name="webBinding">
<security mode="Transport">
<transport clientCredentialType="Basic" />
</security>
</binding>
</basicHttpBinding>
</bindings>
Thanks in advance!

Another option would be to create another cert for client authentication. I'm pretty sure the iPhone supports X509 certs. Just change your config to have a client credential type of "Certificate". This works for basic authentication, but you may still need custom user/name pwd over Basic if you want to uniquely identify every client.

You shouldn't have to override any method. Just declare Digest transport security in your basicHttpBinding:
<bindings>
<basicHttpBinding>
<binding name="SecurityByTransport">
<security mode="Transport">
<transport clientCredentialType="Digest" />
</security>
</binding>
</basicHttpBinding>
</bindings>
In you iPhone app you handle the NSURLConnectionDelegate connection:didReceiveAuthenticationChallenge message as shown in Handling Authentication Challenges:
-(void)connection:(NSURLConnection *)connection
didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
if ([challenge previousFailureCount] == 0) {
NSURLCredential *newCredential;
newCredential=[NSURLCredential credentialWithUser:[self preferencesName]
password:[self preferencesPassword]
persistence:NSURLCredentialPersistenceNone];
[[challenge sender] useCredential:newCredential
forAuthenticationChallenge:challenge];
} else {
[[challenge sender] cancelAuthenticationChallenge:challenge];
}
}

Related

Calling JSON Rest service from browser

Want to enable https on my REST JSON WCF service and test it in IE browser.
WSDL is loading with no issues (https://localhost/myservice/Imyservice.svc?WSDL).
But i tried to call a operation ( https://localhost/myservice/Imyservice.svc/Getdata), I am getting
Request Error The server encountered an error processing the request. .
Below is my web.config. Can anyone help me with this
<webHttpBinding>
<binding name="SecureBasicRest" allowCookies="true" >
<security mode="Transport" />
<readerQuotas maxDepth="32"/>
</binding>
</webHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="svcBehavior">
<serviceMetadata httpsGetEnabled="true" httpGetEnabled="false"/>
<serviceDebug includeExceptionDetailInFaults="false"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="svcEndpoint">
<webHttp helpEnabled="true"/>
</behavior>
</endpointBehaviors>
</behaviors>
<services>
<service name="myservice.Imyservice" behaviorConfiguration="svcBehavior">
<endpoint binding="webHttpBinding" bindingConfiguration="SecureBasicRest"
behaviorConfiguration="svcEndpoint" name="webHttp"
contract="myservice.Imyservice" />
</service>
</services>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
The root cause is that there is something wrong with the definition of the service endpoint.
<service name="myservice.Imyservice" behaviorConfiguration="svcBehavior">
<endpoint binding="webHttpBinding" bindingConfiguration="SecureBasicRest"
behaviorConfiguration="svcEndpoint" name="webHttp"
contract="myservice.Imyservice" />
</service>
Your idea is correct, we should add a service endpoint with transport security mode. However, the service name should be a fully qualified name of the service implemented class instead of the service interface.
<service name="myservice.myservice"
Feel free to let me know if the problem still exists.

Self Hosted (console) WCF configuration

i had a WCF server running through a Console application, it will run as an azure service so i don't to host it in ISS.
I belived it work well and share datas to my web app through ajax request. But when i maked a service that recieve a filestream, i realized that my binding configuration is not used at all. My WCF work same without binding section.
My probleme concerne especially the key "maxReceivedMessageSize" because default value limit me to 8Ko request and it is a problem for uploading file.
Bindings section (that i can comment) :
<webHttpBinding>
<binding name="MyBindingConfig" crossDomainScriptAccessEnabled="true" maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647" transferMode="Buffered">
<readerQuotas maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxDepth="2147483647" maxNameTableCharCount="2147483647" />
<security mode="None" />
</binding>
</webHttpBinding>
Services section :
<service name="MyData.Service.MyDataServiceRestFull">
<endpoint binding="webHttpBinding"
contract="MyData.Service.IMyDataServiceRestFull" bindingName="MyBindingConfig" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:9997/MyDataServer"/>
</baseAddresses>
</host>
</service>
Note that i tryed with basicHttpBinding and i had same issue.
The problem is that you're incorrectly referring to your binding in your <endpoint/> element. You're using the bindingName attribute, when you should be using the bindingConfiguration attribute:
<endpoint binding="webHttpBinding"
contract="MyData.Service.IMyDataServiceRestFull"
bindingConfiguration="MyBindingConfig" />

dotnetopenauth Provider WebConfig Error

I am required to create a provider using the DNOA. I have downloaded the libraries from the DNOA site and attempted to load the oAuthServiceProvider example. I couldn't load this as this is looking for
\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets"
which is part of Visual Studio 2010, I however am on 2008. Despite changing the version to v9.0 it was still looking for v10.0. Nevermind.
I open the project as a website and tried to run this. But now get the error "Error 1 Unrecognized configuration section uri."
Any ideas as to what is going on here? Below is the webconfig
<?xml version="1.0"?>
<!-- The uri section is necessary to turn on .NET 3.5 support for IDN (international domain names),
which is necessary for OpenID urls with unicode characters in the domain/host name.
It is also required to put the Uri class into RFC 3986 escaping mode, which OpenID and OAuth require. -->
<uri>
<idn enabled="All"/>
<iriParsing enabled="true"/>
</uri>
<system.net>
<defaultProxy enabled="true" />
<settings>
<!-- This setting causes .NET to check certificate revocation lists (CRL)
before trusting HTTPS certificates. But this setting tends to not
be allowed in shared hosting environments. -->
<!--<servicePointManager checkCertificateRevocationList="true"/>-->
</settings>
</system.net>
<!-- this is an optional configuration section where aspects of dotnetopenauth can be customized -->
<dotNetOpenAuth>
<!-- Allow DotNetOpenAuth to publish usage statistics to library authors to improve the library. -->
<reporting enabled="true" />
<messaging>
<untrustedWebRequest>
<whitelistHosts>
<add name="localhost"/>
</whitelistHosts>
</untrustedWebRequest>
</messaging>
</dotNetOpenAuth>
<appSettings/>
<connectionStrings>
<system.web>
<!--
Set compilation debug="true" to insert debugging
symbols into the compiled page. Because this
affects performance, set this value to true only
during development.
-->
<compilation debug="true" targetFramework="4.0">
<assemblies>
<remove assembly="DotNetOpenAuth.Contracts"/>
<add assembly="System.Data.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
</assemblies>
</compilation>
<authentication mode="Forms">
<forms name="oauthSP" />
</authentication>
<pages controlRenderingCompatibilityVersion="3.5" clientIDMode="AutoID"/>
</system.web>
<!--
The system.webServer section is required for running ASP.NET AJAX under Internet
Information Services 7.0. It is not necessary for previous version of IIS.
-->
<log4net>
<appender name="TracePageAppender" type="OAuthServiceProvider.Code.TracePageAppender, OAuthServiceProvider">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date (GMT%date{%z}) [%thread] %-5level %logger - %message%newline"/>
</layout>
</appender>
<!-- Setup the root category, add the appenders and set the default level -->
<root>
<level value="INFO"/>
<!--<appender-ref ref="RollingFileAppender" />-->
<appender-ref ref="TracePageAppender"/>
</root>
<!-- Specify the level for some specific categories -->
<logger name="DotNetOpenAuth">
<level value="ALL"/>
</logger>
</log4net>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="DataApiBehavior">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
<serviceAuthorization serviceAuthorizationManagerType="OAuthServiceProvider.Code.OAuthAuthorizationManager, OAuthServiceProvider" principalPermissionMode="Custom"/>
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="DataApiBehavior" name="OAuthServiceProvider.DataApi">
<endpoint address="" binding="wsHttpBinding" contract="OAuthServiceProvider.Code.IDataApi">
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>

Exception: The client certificate is not provided

I am trying to configure WCF service with security. I have generated 2 certificates (for server and client side) stored in LocalComputer\Personal Certificates. My configuration is:
Server:
<netTcpBinding>
<binding name="defaultBinding">
<security mode="Transport">
<transport clientCredentialType="Certificate"/>
</security>
</binding>
</netTcpBinding>
<service name="..." behaviorConfiguration="serviceBehavior">
<endpoint address="..." binding="netTcpBinding" bindingConfiguration="defaultBinding" contract="...">
<identity>
<dns value="ClientSide"/>
</identity>
</endpoint>
</service>
<behavior name="serviceBehavior">
<serviceCredentials>
<serviceCertificate storeLocation="LocalMachine" storeName="My" findValue="ServerSide" x509FindType="FindBySubjectName"/>
<clientCertificate>
<authentication certificateValidationMode="None" revocationMode="NoCheck"/>
</clientCertificate>
</serviceCredentials>
<behavior>
Client:
<netTcpBinding>
<binding name="defaultBinding">
<security mode="Transport">
<transport clientCredentialType="Certificate"/>
</security>
</binding>
</netTcpBinding>
<endpoint name="..." binding="netTcpBinding" bindingConfiguration="defaultBinding" contract="..."
behaviorConfiguration="endpointBehavior">
<identity>
<dns value="ServerSide"/>
</identity>
</endpoint>
<behavior name="endpointBehavior">
<clientCredentials>
<serviceCertificate>
<authentication certificateValidationMode="None" revocationMode="NoCheck"/>
</serviceCertificate>
<clientCertificate storeLocation="LocalMachine" storeName="My" findValue="ClientSide" x509FindType="FindBySubjectName"/>
</clientCredentials>
<behavior>
I am getting the exception: The client certificate is not provided. Specify a client certificate in ClientCredentials
I have tried many tutorials, but none of them works. Any suggestion?
The answer is actually in the Exception.
You don't have a client certificate. You define a service certificate for the client certificate with this
<clientCredentials>
<serviceCertificate>
<authentication certificateValidationMode="None" revocationMode="NoCheck"/>
</serviceCertificate>
<clientCertificate storeLocation="LocalMachine" storeName="My" findValue="ClientSide" x509FindType="FindBySubjectName"/>
</clientCredentials>
But what you actually should have done is defining a client certificate for the client
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="endpointBehavior">
<clientCredentials>
<clientCertificate storeLocation="LocalMachine" storeName="My" findValue="ClientSide" x509FindType="FindBySubjectName" />
<serviceCertificate>
<authentication certificateValidationMode="None" revocationMode="NoCheck" />
</serviceCertificate>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
This should at least solve your The client certificate is not provided. Specify a client certificate in ClientCredentials exception.

Unable to set maxReceivedMessageSize through web.config

I have now investigated the 400 - BadRequest code for the last two hours.
A lot of sugestions goes towards ensuring the bindingConfiguration attribute is set correctly, and in my case, it is.
Now, I need YOUR help before destroying the building i am in :-)
I run a WCF RestFull service (very lightweight, using this resource for inspiration: http://msdn.microsoft.com/en-us/magazine/dd315413.aspx) which (for now) accepts an XmlElement (POX) provided through the POST verb.
I am currently ONLY using Fiddler's request builder before implementing a true client (as this is mixed environments).
When I do this for XML smaller than 65K, it works fine - larger, it throws this exception:
The maximum message size quota for incoming messages (65536) has been exceeded. To increase the quota, use the MaxReceivedMessageSize property on the appropriate binding element.
Here is my web.config file (which I even included the client-tag for (desperate times!)):
<system.web>
<httpRuntime maxRequestLength="1500000" executionTimeout="180"/>
</system.web>
<system.serviceModel>
<diagnostics>
<messageLogging logEntireMessage="true" logMalformedMessages="true" logMessagesAtServiceLevel="true" logMessagesAtTransportLevel="true" />
</diagnostics>
<bindings>
<webHttpBinding>
<binding name="WebHttpBinding" maxReceivedMessageSize="1500000" maxBufferPoolSize="1500000" maxBufferSize="1500000" closeTimeout="00:03:00" openTimeout="00:03:00" receiveTimeout="00:10:00" sendTimeout="00:03:00">
<readerQuotas maxStringContentLength="1500000" maxArrayLength="1500000" maxBytesPerRead="1500000" />
<security mode="None"/>
</binding>
</webHttpBinding>
</bindings>
<client>
<endpoint address="" binding="webHttpBinding" bindingConfiguration="WebHttpBinding" contract="Commerce.ICatalogue"/>
</client>
<services>
<service behaviorConfiguration="ServiceBehavior" name="Catalogue">
<endpoint address=""
behaviorConfiguration="RestFull"
binding="webHttpBinding"
bindingConfiguration="WebHttpBinding"
contract="Commerce.ICatalogue" />
<!-- endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" / -->
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="RestFull">
<webHttp/>
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="ServiceBehavior">
<serviceDebug httpHelpPageEnabled="true" includeExceptionDetailInFaults="true"/>
<serviceMetadata httpGetEnabled="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
Thanks in advance for any help leading to succesfull call with >65K XML ;-)
All right, this one really caused me a hard time resolving, which I will spare others for.
The challenge was in the fact, that I used the <%# ServiceHost Factory="System.ServiceModel.Activation.WebServiceHostFactory" Service="fullyQualifiedClassName" %>, which is a nice and easy factory implementation approach.
However, this approach has it drawbacks; since no configuration is needed in the web.config file, the WebServiceHostFactory class by design does not ever read from the web.config file.
I know; I could inherit from this class, and make the appropriate changes so it may indeed read from the config file, but this seemed a little out of scope.
My solution was to go back to the more traditional way of implementing the WCF; <%# ServiceHost Service="fullyQualifiedClassName" CodeBehind="~/App_Code/Catalogue.cs" %>, and then use my already configured values in the web.config file.
Here is my modified web.config file (with respect to Maddox headache):
<system.serviceModel>
<bindings>
<webHttpBinding>
<binding name="XmlMessageBinding" maxReceivedMessageSize="5000000" maxBufferPoolSize="5000000" maxBufferSize="5000000" closeTimeout="00:03:00" openTimeout="00:03:00" receiveTimeout="00:10:00" sendTimeout="00:03:00">
<readerQuotas maxStringContentLength="5000000" maxArrayLength="5000000" maxBytesPerRead="5000000" />
<security mode="None"/>
</binding>
</webHttpBinding>
</bindings>
<services>
<service name="fullyQualifiedClassName" behaviorConfiguration="DevelopmentBehavior">
<endpoint name="REST" address="" binding="webHttpBinding" contract="fullyQualifiedInterfaceName" behaviorConfiguration="RestEndpointBehavior" bindingConfiguration="XmlMessageBinding" />
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="RestEndpointBehavior">
<webHttp/>
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="DevelopmentBehavior">
<serviceDebug httpHelpPageEnabled="true" includeExceptionDetailInFaults="true"/>
<serviceMetadata httpGetEnabled="true"/>
</behavior>
<behavior name="ProductionBehavior">
<serviceDebug httpHelpPageEnabled="false" includeExceptionDetailInFaults="false"/>
<serviceMetadata httpGetEnabled="false"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
Another benefit of this change is, that you can now reference your WCF-rest service directly from .NET; this cannot be done using the Factory model and my implementation of XmlElement through out the solution.
I hope this can help others with similar issues ...
I know this is a very old Question and it already has an answer...
Anyway...
What I did to solve this "issue" I created a Factory inherited from WebServiceHostFactory and created a Custom Service Host inherited from WebServiceHost
And in the host I overrode the OnOpening method like this
protected override void OnOpening()
{
base.OnOpening();
foreach (var endpoint in Description.Endpoints)
{
var binding = endpoint.Binding as System.ServiceModel.Channels.CustomBinding;
foreach (var element in binding.Elements)
{
var httpElement = element as System.ServiceModel.Channels.HttpTransportBindingElement;
if (httpElement != null)
{
httpElement.MaxBufferSize = 2147483647;
httpElement.MaxReceivedMessageSize = 2147483647;
}
}
}
}
I think i had the same issue, but when i configured the default-binding for webHttp then it worked:
<bindings>
<webHttpBinding>
<binding maxReceivedMessageSize="2000000"
maxBufferSize="2000000">
<readerQuotas maxStringContentLength="2000000"/>
</binding>
</webHttpBinding>
</bindings>
Observe: no name on the binding.
This is a blog entry I wrote that reproduces this problem with an absolutely minimal WCF server and client piece:
WCF - Fixing client side string length exceptions
In particular, you may need a Custom Binding Configuration. At least reproducing this sample may give you some ideas for your particular situation.