Xamarin + WCF + SSL + Transport + Certificate - c#

I have a Net Framework 4.5 WCF Service, running with async/task methods. It is deployed on a valid URL, with a correct Digicert certificate, assuring the domain. We have a "client certificate", with a "one-to-one" mapping, and all its ok for our "Winforms" apps.
Now, we wan't to call it from our Android/iOS Xamarin projects.
We know that Xamarin doesn't supports wsBinding, so we're are using this config:
Server
<system.serviceModel>
<services>
<service
name="serviceWCF.nameService"
behaviorConfiguration="behavior_base">
<endpoint address=""
binding="basicHttpBinding"
bindingConfiguration="transport"
contract="serviceWCF.nameInterfaceService" />
</service>
</services>
<bindings>
<basicHttpBinding>
<binding name="transport">
<security mode="Transport" >
<transport clientCredentialType="Certificate"/>
</security>
</binding>
</basicHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="behavior_base">
<serviceMetadata httpsGetEnabled="true" httpsGetUrl=""/>
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
</system.serviceModel>
We created a proxy from SVCUTIL.EXE, then we have implement manually the async methods, channel creation, because Xamarin doesn't supports dinamic bindings, and so on.
The proxy for our Xamarin client app, it's invoked so:
BasicHttpBinding binding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
AddressHeader addressHeader2;
AddressHeader[] addressHeaders;
EndpointAddress endpoint;
addressHeader2 = AddressHeader.CreateAddressHeader("nameapp_iOS", "https:\\URL_WCF_Service.svc", 0);
addressHeaders = new AddressHeader[]{ addressHeader2};
endpoint = new EndpointAddress(new System.Uri("https:\\URL_WCF_Service.svc"),addressHeaders);
System.Security.Cryptography.X509Certificates.X509Certificate2 oCert;
oCert = new System.Security.Cryptography.X509Certificates.X509Certificate2(System.IO.File.ReadAllBytes("CertBundle.pfx"), "pass");
Service_MovilClient oProxy = new Service_MovilClient(binding, endpoint);
Service_MovilClient oProxy.ClientCredentials.ClientCertificate.Certificate = oCert
But ... nothing happens... time out....
The server it's ok. The url can be accessed from the iOS emulator. We can use it with only "basicHttpBinding", but, we want to use SSL+Client Certificate.
Any ideas? Now I'm stuck.

It's worthless to spoil more efforts. By now, WCF Xamarin its very short.
I have to settle for with HTTPs and a basic Transport Security (security mode="Transport").
I have to use that Wcf services ... But if you, pathetic human, are reading this prior a new development, use REST services. They have a much better support form Xamarin.

Related

WCF / wsHttpBinding / Message Security - BadTokenRequest

So got an older WCF service / client I'm working on. Added a new (static) logging system to it, actually and now doing some load testing.
Getting some really annoying sporadic issues now - claiming "Secure channel cannot be opened because security negotiation with the remote endpoint has failed". I noticed I get a CommunicationException with a fault name of Sender and subcode of BadContextToken.
Weird thing is, I'll get 2-4 correct responses, then a flurry of these exceptions, then start getting good responses again.
This is my first real foray into WCF, and not loving it so far :)
Service web.config:
<system.serviceModel>
<bindings>
<wsHttpBinding>
<security mode="Message">
<message clientCredentialType="UserName" />
</security>
</wsHttpBinding>
</bindings>
<services>
<service behaviorConfiguration="ServiceBehavior" name="MyNamespace.MyService">
<endpoint address="" binding="wsHttpBinding" contract="MyNamespace.IMyService" bindingConfiguration="wsMessage">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="ServiceBehavior">
<serviceMetadata httpGetEnabled="false" />
<serviceCredentials>
<serviceCertificate findValue="MyValue" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" />
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="MyNamespace.UserNamePassValidator, MyNamespace" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
And on the client side, the client is instantiated as such:
var binding = new WSHttpBinding();
binding.Name = "WSHttpBinding_IMyService";
binding.Security.Mode = SecurityMode.Message;
binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
var client = new MyService(binding, "http://myserver:8080/myapp/service.svc");
var endpointIdentity = new DnsEndpointIdentity("MyValue"); // Match the certificate name used by the server
client.Endpoint.Address = new EndpointAddress(new Uri("http://myserver:8080/myapp/service.svc"), endpointIdentity, client.Endpoint.Address.Headers);
var creds = client.ClientCredentials;
creds.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;
creds.UserName.UserName = "myuser";
creds.UserName.Password = "mypassword";
string retVal = client.SendRequest(); // SendRequest == one of the methods on my IMyService, returns a string. This is also where I sporadically see my error when load testing.
I would appreciate any pointers to help me out with this WCF setup!
These might be useful additions to your web.config:
<behaviors>
<serviceBehaviors>
<behavior name="CalculatorServiceBehavior">
<serviceDebug includeExceptionDetailInFaults="False" />
<serviceMetadata httpGetEnabled="True"/>
<serviceThrottling maxConcurrentCalls="20" maxConcurrentInstances="100"/>
</behavior>
</serviceBehaviors>
</behaviors>
<binding name="basicHttp" allowCookies="true" maxReceivedMessageSize="1048576" maxBufferSize="1048576" maxBufferPoolSize="1048576">
<readerQuotas maxDepth="32" maxArrayLength="1048576" maxStringContentLength="1048576"/>
</binding>
Usually this kind of "random" behaviour might depend on:
Timeouts (probably not your case, since you'd get a different exception)
Too many connections: if you client opens too many connections (and "forgets" to close them), you'll exceed the default allowed maximum (depending on context, it might be 10 connections).
You can act on this if you alter your web.config, editing maxConcurrentCalls and maxConcurrentInstances
Perhaps those errors are not random, but specific to some message; if so, that might be due to its size (i.e. it's too large): again, alter your web.config setting maxReceivedMessageSize, maxBufferSize, maxBufferPoolSize and readerQuotas
Of course you will get more info if you turn on WCF tracing.

WCF Serice - Getting 400 Bad Request but can use IE to browse to it

I have written a WCF Service which is being called by a Winforms application.
I have tested calling the WCF Service from the WinForms application locally and it all works fine
I have moved both the WinForms application and the WCF Service to a Remote Server, on the Remote Server I can use IE to browse to the Service but when I try to use the Winforms application I get a 400 Bad Response error.
My local machine and the Remote Server has been configured exactly the same, Windows Firewall, User accounts etc and the codebase/config files of both the Service and Winforms app are the same.
The config file for the WCF Service is as follows (i've had to remove the base address)
<bindings>
<webHttpBinding>
<binding name="webBinding">
<security mode="None">
<transport clientCredentialType="None" />
</security>
</binding>
</webHttpBinding>
</bindings>
<services>
<service behaviorConfiguration="CodeLocksAPIServiceBehavior" name="CodeLocksAPI.WCF.CodeLocksAPIService">
<host>
<baseAddresses>
</baseAddresses>
</host>
<endpoint address="" behaviorConfiguration="webHttpBehavior" binding="webHttpBinding" bindingConfiguration="webBinding" contract="CodeLocksAPI.WCF.ICodeLocksAPIService" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="CodeLocksAPIServiceBehavior">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="webHttpBehavior">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
In the Winforms app config I have tried adding the DefaultProxy tabs which do not help
WCF Service is called from Code Using the following
string serviceUserName = ConfigurationManager.AppSettings["ServiceUserName"];
string servicePassword = ConfigurationManager.AppSettings["ServicePassword"];
HttpClientHandler handler = new HttpClientHandler
{
Credentials = new
System.Net.NetworkCredential(serviceUserName, servicePassword)
};
this.Client = new HttpClient(handler);
this.Client.BaseAddress = new Uri(this.GetBaseAddressFromConfig());
processInitializeLockRequestArgs.AssetRef = assetRef;
string contentMessage = Newtonsoft.Json.JsonConvert.SerializeObject(processInitializeLockRequestArgs);
byte[] contentBytes = System.Text.Encoding.UTF8.GetBytes(contentMessage);
ByteArrayContent content = new ByteArrayContent(contentBytes);
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");
httpResponse = this.Client.PostAsync(methodUri, content).Result;
As already stated - this code is working locally but not on the remote server - the WCF Service can be browsed to locally and on the remote service
I've been informed that the issue is most likely down to Proxy settings on the WinForms application and that the issue is not down to a Firewall or anything like that
You could try adding
<configuration>
<system.net>
<defaultProxy useDefaultCredentials="true"/>
</system.net>
....
to the beginning of your WinForms config file.

WCF Windows Authentication, local debug ok but not work on server

On my Server, running a Windows Authentication WCF application hosted in IIS 7:
IIS Config: Windows Auth : disabled; anonymous: enabled
<system.serviceModel>
<services>
<service name="Services.AccountService" behaviorConfiguration="MyServiceTypeBehaviors">
<endpoint address="" binding="ws2007HttpBinding" bindingConfiguration="AccountServiceBinding"
contract="Contracts.IAccountService" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="MyServiceTypeBehaviors" >
<!--<serviceDebug includeExceptionDetailInFaults="true"/>-->
<serviceMetadata httpGetEnabled="true" httpGetUrl="" />
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<ws2007HttpBinding>
<binding name="AccountServiceBinding" >
<security mode="Message">
<message clientCredentialType="Windows" />
</security>
</binding>
</ws2007HttpBinding>
</bindings>
</system.serviceModel>
and then I create a MVC4 Application with following code to call WCF service:
WS2007HttpBinding myBinding = new WS2007HttpBinding();
myBinding.Security.Mode = SecurityMode.Message;
EndpointAddress endpoint = new EndpointAddress("http://server/accountservice.svc");
AccountServiceClient _client = new AccountServicesObjects.AccountServiceClient(myBinding, endpoint);
_client.ClientCredentials.Windows.ClientCredential.UserName = "username";
_client.ClientCredentials.Windows.ClientCredential.Password = "password";
var user = _client.GetUserInformation(); // works fine
After I finished this mvc4 application, I deploy this website to the Same server which is running WCF one, when I login, occurs:
System.ServiceModel.Security.SecurityNegotiationException: The caller was not authenticated by the service.
System.ServiceModel.FaultException:The request for security token could not be satisfied because authentication failed.
on System.ServiceModel.Security.SecurityUtils.ThrowIfNegotiationFault(Message message, EndpointAddress target)
on System.ServiceModel.Security.SspiNegotiationTokenProvider.GetNextOutgoingMessageBody(Message incomingMessage, SspiNegotiationTokenProviderState sspiState)
how this happend?
It seems your service hasn't been started at all. If you enable tracing you can find an exception. There is an error in your configuration - you have no base address, but you have set HttpGetUrl to true (for that option you have to set base address)
This should work for you:
<service name="Services.AccountService" behaviorConfiguration="MyServiceTypeBehaviors">
<endpoint address="accountservice.svc" binding="ws2007HttpBinding"
bindingConfiguration="AccountServiceBinding"
contract="Services.IAccountService" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:8000/"/>
</baseAddresses>
</host>
</service>

WCF custom certificate validation with BasicHttpBinding

I have a WCF application hosted on IIS 6 that needs to
Have 2-way SSL authentication
Validate client certificate content with some client host information
Validate client certificate is issued by the valid subCA.
I was able to do 1) successfully. I am trying to achieve 2) and 3) by following this - basically creating a class that inherits X509CertificateValidator and overriding the Validate method with my own validation implementation(step 2 and 3). I followed the MSDN instructions exactly however, it seem that the Validate method is not being called. I purposely throw a SecurityAccessDeniedException in the overidden Validate method and no exception is thrown when I tried to access the service via my browser. I can still access my website with any client certificate.
I also read this thread but it didn't really help. Any help would be greatly appreciated!
Here's my configuration:
<system.serviceModel>
<services>
<service behaviorConfiguration="SimpleServiceBehavior"
name="SampleNameSpace.SampleClass">
<endpoint address=""
binding="basicHttpBinding"
bindingConfiguration="NewBinding0"
contract="SampleNameSpace.ISampleClass" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="SimpleServiceBehavior">
<serviceMetadata httpsGetEnabled="true" policyVersion="Default" />
<serviceCredentials>
<clientCertificate>
<authentication certificateValidationMode="Custom" customCertificateValidatorType="SampleNameSpace.MyX509CertificateValidator, SampleAssembly"/>
</clientCertificate>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<basicHttpBinding>
<binding name="NewBinding0">
<security mode="Transport">
<transport clientCredentialType="Certificate" />
</security>
</binding>
</basicHttpBinding>
</bindings>
You can also try to override certificate validation with this ServerCertificateValidationCallback
We are using it with WCF HttpBinding in such way:
System.Net.ServicePointManager.ServerCertificateValidationCallback =
(sender, certificate, chain, policyErrors) =>
{
var isValid = false;
// some checking logic
return isValid;
};

Bad response (400) with a WCF streaming service configured programmatically

this if my first attempt at using streaming for WCF, and I am struggling with the dreadful "The remote server returned an unexpected response: (400) Bad Request" response.
The trace viewer says that this is a System.ServiceModel.ProtocolException with message "There is a problem with the XML that was received from the network. See inner exception for more details." The inner exception type says "The body of the message cannot be read because it is empty."
Leaving everything else equal, if I switch to buffered mode on the client side, I am able to debug into the server code!
For some reason, I have to configure my service programmatically, as follows:
public IUniverseFileService OpenProxy(string serviceUrl)
{
Debug.Assert(!string.IsNullOrEmpty(serviceUrl));
var binding = new BasicHttpBinding();
binding.Name = "basicHttpStream";
binding.MaxReceivedMessageSize = 1000000;
binding.TransferMode = TransferMode.Streamed;
var channelFactory =
new ChannelFactory<localhost.IUniverseFileService>(
binding,
new EndpointAddress(serviceUrl));
return channelFactory.CreateChannel();
}
While the server is configured as follows:
<system.serviceModel>
<!-- BEHAVIORS -->
<behaviors>
<serviceBehaviors>
<behavior name="serviceBehavior">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true" httpHelpPageEnabled="true"/>
<dataContractSerializer maxItemsInObjectGraph="2147483647"/>
</behavior>
</serviceBehaviors>
</behaviors>
<!-- SERVICES -->
<services>
<service behaviorConfiguration="serviceBehavior" name="Org.Acme.UniverseFileService">
<endpoint address=""
binding="basicHttpBinding"
name="basicHttpStream"
bindingConfiguration="httpLargeMessageStream"
contract="Org.Acme.RemoteCommand.Service.IUniverseFileService" />
<endpoint address="mex"
binding="mexHttpBinding"
bindingConfiguration="" name="mexStream"
contract="IMetadataExchange"/>
</service>
</services>
<!-- BINDINGS -->
<bindings>
<basicHttpBinding>
<binding name="httpLargeMessageStream"
maxReceivedMessageSize="2147483647"
maxBufferPoolSize="2147483647"
maxBufferSize="2147483647"
transferMode="Streamed"/>
</basicHttpBinding>
</bindings>
I appreciate your help!
Stefano
Everything started to work when I changed the transfer mode from Streamed to StreamedResponse as follows:
binding.TransferMode = TransferMode.StreamedResponse;
Still I don't understand why this works and Streamed does not, and why I am able to both send and receive a file stream from the server.
Streaming mode is not supported by ASP.NET development server. You need to deploy the service to IIS (or a WCF Service Application) to use Streaming mode.
Try to add messageEncoding="Mtom" in bining tag.

Categories