Bug 8020 - Mono for Android Secure WCF Service (Username Authentication)
Summary: Mono for Android Secure WCF Service (Username Authentication)
Status: RESOLVED FEATURE
Alias: None
Product: Android
Classification: Xamarin
Component: BCL Class Libraries ()
Version: 4.2.x
Hardware: PC Windows
: --- normal
Target Milestone: ---
Assignee: Jonathan Pryor
URL:
Depends on:
Blocks:
 
Reported: 2012-10-26 12:10 UTC by Lee Gerrard
Modified: 2012-11-14 12:40 UTC (History)
4 users (show)

Tags:
Is this bug a regression?: ---
Last known good build:


Attachments
MonoDroid WCF Security Issue Reproduction Solution (4.06 MB, application/x-zip-compressed)
2012-10-29 07:36 UTC, Lee Gerrard
Details
Screenshot of error on Android emulator (174.66 KB, image/png)
2012-10-29 07:38 UTC, Lee Gerrard
Details


Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report on Developer Community or GitHub with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:
Status:
RESOLVED FEATURE

Description Lee Gerrard 2012-10-26 12:10:08 UTC
The company I work for is currently developing an application in .NET that will run on Windows Phone, Android and IPhone. MonoDroid and MonoTouch will be used so the majority of code can be shared between platforms.

The application calls a WCF IIS hosted service (using basicHttpBinding and SSL). With each service call a username and password is sent using the ClientCredentials object on the client proxy. See example below

var binding = new BasicHttpBinding
                {
                    Name = "basicHttpBinding",
                    MaxReceivedMessageSize = 67108864,
                    ReaderQuotas =
                        new System.Xml.XmlDictionaryReaderQuotas() { MaxArrayLength = 2147483646, MaxStringContentLength = 5242880 }
                };


            binding.Security.Mode = BasicHttpSecurityMode.TransportWithMessageCredential;
            binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.UserName;

            var endpoint = new EndpointAddress("http://example.wcfservice/service.svc");

            using (Example.WcfClient client = new WcfClient(binding, endpoint))
            {
                client.ClientCredentials.UserName.UserName = "bob";
                client.ClientCredentials.UserName.Password = "pass";

                var res = client.WcfServiceMethod();
            } 

Web.Config for service

<service behaviorConfiguration="externalServiceBehavior" name=" Example.WcfService">
        <endpoint address="" binding="basicHttpBinding" bindingConfiguration="basicUsrPassHttps" contract=" Example.IWcfService " />
      </service>

<binding name="basicUsrPassHttps" allowCookies="false" maxReceivedMessageSize="200000" maxBufferSize="200000" maxBufferPoolSize="200000">
          <security mode="TransportWithMessageCredential">
            <message clientCredentialType="UserName"/>
          </security>
          <readerQuotas maxDepth="32" maxArrayLength="200000" maxStringContentLength="200000"/>
        </binding>

<behavior name="externalServiceBehavior">
          <serviceDebug includeExceptionDetailInFaults="false"/>
          <serviceMetadata httpGetEnabled="false" httpsGetEnabled="true" />
          <serviceCredentials>
            <userNameAuthentication userNamePasswordValidationMode="MembershipProvider" membershipProviderName="ExampleUserMembershipProvider"/>
          </serviceCredentials>
          <serviceAuthorization principalPermissionMode="UseAspNetRoles" roleProviderName="ExampleUserRoleProvider" />
        </behavior>

The MonoDroid application is throwing an exception when the WCF service method is called. See exception below.

Unhandled Exception:
	
System.ServiceModel.FaultException: An error occurred when verifying security for the message.

Turning on WCF logging and inspecting the message sent to the service I can see there is no security details sent with the method call. 

Microsoft Service Trace Viewer indicates from the logs that WCF throws an System.ServiceModel.Security.MessageSecurityException with a message containing;

“Security processor was unable to find a security header in the message. This might be because the message is an unsecured fault or because there is a binding mismatch between the communicating parties.   This can occur if the service is configured for security and the client is not using security.”

Missing security details from WCF logs (This is from a console application calling the same service. The <o:Username> and <o:Password> are present when calling from the console application but not there when calling from the MonoDroid application)

<MessageHeaders>
<o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<u:Timestamp u:Id="_0" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<u:Created>2012-10-26T15:18:34.603Z</u:Created>
<u:Expires>2012-10-26T15:23:34.603Z</u:Expires>
</u:Timestamp>
<o:UsernameToken u:Id="uuid-2c1a8fa6-1759-4293-9afe-2d3535437fe2-1" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<o:Username>
<!-- Removed-->
</o:Username>
<o:Password>
<!-- Removed-->
</o:Password>
</o:UsernameToken>
</o:Security>
<To d4p1:mustUnderstand="1" xmlns:d4p1="http://schemas.xmlsoap.org/soap/envelope/" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">https://tbs-usr-dev18.tbsmobility.local/TMHost.Comms.Service/ConfigurationInternet.svc</To>
<Action d4p1:mustUnderstand="1" xmlns:d4p1="http://schemas.xmlsoap.org/soap/envelope/" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://tempuri.org/IConfiguration/GetCompanionSetting</Action>
</MessageHeaders>

Mono for Android version: 4.2.6
Target Android API Level: 16

This problem sounds very similar to this issue: http://mono-for-android.1047100.n5.nabble.com/Mono-for-Android-s-secure-WCF-services-td5696645.html
Comment 1 Lee Gerrard 2012-10-29 07:36:03 UTC
Created attachment 2805 [details]
MonoDroid WCF Security Issue Reproduction Solution

This zip file contains a Visual Studio 2010 solution containing WCF service, MonoDroid and Windows Console projects reproducing the issue.

The Windows Console program shows a working example while the MonoDroid project shows calling a WCF service with client credentials does not work.

The website containing the service had a self signed certificate installed. This was used for the SSL certificate. The following website was used to create the certificate: http://wcfsecurity.codeplex.com/wikipage?title=How%20To%20-%20Create%20and%20Install%20Temporary%20Certificates%20in%20WCF%20for%20Transport%20Security%20during%20Development&referringTitle=How%20Tos

The MonoDroid project requires the machine name defining in the service url. This is the machine where the Android service is installed.
Comment 2 Lee Gerrard 2012-10-29 07:38:02 UTC
Created attachment 2806 [details]
Screenshot of error on Android emulator
Comment 3 Lee Gerrard 2012-11-05 05:39:37 UTC
The attached reproduction solution has the following code missing from the Android client project just before creating the client proxy (i.e. ServiceClient)

binding.Security.Mode =
BasicHttpSecurityMode.TransportWithMessageCredential;
            binding.Security.Message.ClientCredentialType =
BasicHttpMessageCredentialType.UserName;

Adding this into the code still does not make it work (as observed when logging this issue).

Have just tried running the solution with the last stable MonoDroid release 4.2.7. The issue is still happening.
Comment 4 Geoff Aldridge 2012-11-12 20:29:35 UTC
I'm getting the same issue in an application that I'm writing.

Any thoughts on a way to get around this? Is it possible to manually inject the data into the request header without too much effort?

There are ways in WCF to add stuff to the header but this would involve replacing or adding to the security section.

Regards, Geoff.
Comment 5 Lee Gerrard 2012-11-13 06:53:35 UTC
Hi Geoff

I tried adding the security details via the AddressHeader when constructing the 'EndpointAddress' object but this does not work either.

I have been in touch with the people from Mono and they will not be fixing the issue.

One way to call the service would be to construct the soap request manually using the HttpWebRequest object. See below (you need to update the created and expires dates in the Timestamp section in the soap string);

HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://<machine name>/AndroidService/service.svc");

String xmlString = @"<s:Envelope xmlns:s=""http://schemas.xmlsoap.org/soap/envelope/"" xmlns:u=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd""><s:Header><o:Security s:mustUnderstand=""1"" xmlns:o=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd""><u:Timestamp u:Id=""_0""><u:Created>2012-11-08T17:19:00.000Z</u:Created><u:Expires>2012-11-08T17:35:00.000Z</u:Expires></u:Timestamp><o:UsernameToken u:Id=""uuid-7d650430-e283-4838-a5db-ae835b297681-1""><o:Username>Test</o:Username><o:Password Type=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText"">pass</o:Password></o:UsernameToken></o:Security></s:Header><s:Body><GetData xmlns=""http://tempuri.org/""><value>7</value></GetData></s:Body></s:Envelope>";

ASCIIEncoding encoding = new ASCIIEncoding();

byte[] bytesToWrite = encoding.GetBytes(xmlString);

request.Method = "POST";
request.ContentLength = bytesToWrite.Length;
request.Headers.Add("SOAPAction: \"http://tempuri.org/IService/GetData\"");
request.ContentType = "text/xml; charset=utf-8";

Stream newStream = request.GetRequestStream();
newStream.Write(bytesToWrite, 0, bytesToWrite.Length);
newStream.Close();

HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Stream dataStream = response.GetResponseStream();
StreamReader reader = new StreamReader(dataStream);

string responseFromServer = reader.ReadToEnd();

Hope this helps

Lee
Comment 6 Geoff Aldridge 2012-11-13 16:11:59 UTC
Hi Lee,

Thanks for the response. I was kind of expecting we'd have to hand code this if we want to use this type of communications.

I tried with the IClientMessageInspector and IEndpointBehavior too but it does not give us access to the security section.

Cheers, Geoff.
Comment 7 Atsushi Eno 2012-11-14 12:40:36 UTC
It is not doable in any sense. Monodroid WCF is by no means to implement .NET full WCF. It is based on Moonlight, which used to implement Silverlight, and Silverlight never supported WS-Security.
Besides, mono never finished WS-Security support implementation, and it's not something you could expect to have implemented within a few months, so it's not something that can be imported handy.

You might be still able to grab mono WCF sources and reuse some of them to hack your own version of WS-Security support like this:
http://geekswithblogs.net/SunnyCoder/archive/2009/03/15/username-password-amp-ws-security-with-silverlight.aspx

But not in mono and monodroid themselves.