2

A web service request over SSL raises a WebException on Monotouch v4.0.4.1:

'Error getting response stream (Write: The authentication or decryption has failed)'

Since the server's SSL certificate is self-signed (and btw I think it is not X.509), I am bypassing the certificate validation using ServicePointManager.ServerCertificateValidationCallback. The exact same code works fine on Windows .NET, where the web service call returns the correct result. On Monotouch adding a Writeline shows that the ServerCertificateValidationCallback delegate code is never reached.

Note: Although probably not relevant, the content of the request is SOAP with embedded WS-Security UsernameToken.

  1. Has anyone got something like this to work on MonoTouch? Have seen reports of similar symptom but no resolution. The code and stacktrace are below, any comment appreciated. Can email a self-contained test case if wanted.

  2. I gather there is an alternative approach using certmgr.exe to store the self-signed server certificate in the local trust store, but can't seem to find that app in the MonoTouch distribution. Could anyone point me to it?

..

public class Application
{
    static void Main (string[] args)
    {
        UIApplication.Main (args);
    }
}

// The name AppDelegate is referenced in the MainWindow.xib file.
public partial class AppDelegate : UIApplicationDelegate
{
    // This method is invoked when the application has loaded its UI and its ready to run
    public override bool FinishedLaunching (UIApplication app, NSDictionary options)
    {
        // If you have defined a view, add it here:
        // window.AddSubview (navigationController.View);

        string soapResponse;
        string soapRequest = @" SOAP envelope is here but omitted for brevity ";
        soapResponse = WebService.Invoke("myOperation", soapRequest);
        window.MakeKeyAndVisible ();
        return true;
    }

    // This method is required in iPhoneOS 3.0
    public override void OnActivated (UIApplication application)
    {
    }
}


public class WebService
{
    public static string Invoke(string operation, string soapRequest)
     // Input parameters: 
    //  operation = WS operation name
    //  soapRequest = SOAP XML request
    // Output parameter:
    //  SOAP XML response
    {
        HttpWebResponse response;
        try
        {
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
            ServicePointManager.ServerCertificateValidationCallback = (sender, cert, chain, ssl) => true;
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://myserver.com:7570/MyEndpoint");
            request.Method = "POST";
            request.Headers.Add("SOAPAction", "/MyEndpoint/" + operation);
            request.ContentType = "text/xml;charset=UTF-8";
            request.UserAgent = "Smartphone";
            request.ContentLength = soapRequest.Length;
            request.GetRequestStream().Write(System.Text.Encoding.UTF8.GetBytes(soapRequest), 0, soapRequest.Length);
            request.GetRequestStream().Close();
            response = (HttpWebResponse)request.GetResponse();
            using (StreamReader reader = new StreamReader(response.GetResponseStream(), System.Text.Encoding.UTF8))
            {
                return reader.ReadToEnd();
            }
        }
        catch (WebException e)
        {
            throw new WebException(e.Message);
        }
    }
} 

Stack trace (some names changed to protect the innocent, original available on request):

WS.WebService.Invoke (operation="myOperation", soapRequest="<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\" \n\txmlns:ns1=\"http://mycompany/Common/Primitives/v1\" \n\txmlns:ns2=\"http://mycompany/Common/actions/externals/Order/v1\" \n\txmlns:ns3=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\">\n\t<SOAP-ENV:Header> <wsse:Security SOAP-ENV:mustUnderstand=\"1\" \n\txmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\"> \n\t<wsse:UsernameToken wsu:Id=\"UsernameToken-1\" \n\txmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\"> \n\t<wsse:Username>myusername</wsse:Username> <wsse:Password \n\tType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText\">mypw</wsse:Password> \n\t<wsse:Nonce>{0}</wsse:Nonce> \n\t<wsu:Created xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\">{1}</wsu:Created> \n\t</wsse:UsernameToken> </wsse:Security> \n\t</SOAP-ENV:Header><SOAP-ENV:Body><ns2:tp_getOrderDetailRequest><ns2:header><ns1:source>TEAM</ns1:source>\n\t<ns1:userAccessKey>12345678901234567</ns1:userAccessKey></ns2:header>\n\t<ns2:OrderId>myid1</ns2:OrderId>\n\t<ns2:OrderId>myid2</ns2:OrderId>\n\t</ns2:tp_getOrderDetailRequest>\n\t</SOAP-ENV:Body>\n\t</SOAP-ENV:Envelope>") in /Users/billf/Projects/WS/WS/Main.cs:103
WS.AppDelegate.FinishedLaunching (app={MonoTouch.UIKit.UIApplication}, options=(null)) in /Users/billf/Projects/WS/WS/Main.cs:52
MonoTouch.UIKit.UIApplication.Main (args={string[0]}, principalClassName=(null), delegateClassName=(null)) in /Developer/MonoTouch/Source/monotouch/monotouch/UIKit/UIApplication.cs:26
MonoTouch.UIKit.UIApplication.Main (args={string[0]}) in /Developer/MonoTouch/Source/monotouch/monotouch/UIKit/UIApplication.cs:31
WS.Application.Main (args={string[0]}) in /Users/billf/Projects/WS/WS/Main.cs:18
BillF
  • 444
  • 1
  • 9
  • 15

2 Answers2

2

1) An untrusted root certificate is not the only problem that could result in this exception.

ServicePointManager.ServerCertificateValidationCallback = (sender, cert, chain, ssl) => true;

Add a Console.WriteLine in there so you'll see if it gets called (or not).

throw new WebException(e.Message);

and another here, with full stack trace (not just the Message property).

2) Each application is isolated. This means that:

  • applications cannot updates the global iOS certificate stores (that would create security issues);

  • if a certmgr tool existed (for MT) it could only use a local (mono) store that would be usable only for itself (which would not be of any help for your own apps)

poupou
  • 43,413
  • 6
  • 77
  • 174
  • Yes, poupou, I should have mentioned that adding a Writeline shows that the ServerCertificateValidationCallback delegate is never called. Neither is it called with non-lambda ways of expressing the delegate. – BillF Aug 04 '11 at 01:28
  • That's weird, MonoTouch code for iOS differs from Mono code (to use the device certificate store) but I was sure it implemented the same callbacks. Can you try the (older) ICertificatePolicy ? See http://www.mono-project.com/UsingTrustedRootsRespectfully#Approach_.23-1:_Actively_ignore_security_concerns_.3D for details – poupou Aug 04 '11 at 01:37
  • Will try ICertificate. Stack trace also added above. – BillF Aug 04 '11 at 01:51
  • ICertificatePolicy fails in the same way. Would it be useful if I sent you my self-contained test case? – BillF Aug 04 '11 at 02:16
  • Yep, please fill a bug report at bugzilla.xamarin.com. You can mark the bug (or attachment) as private if required. Thanks! – poupou Aug 04 '11 at 03:09
  • >> Each application is isolated. << So MT relies on an IOS store to know about trusted roots like Verisign? That IOS store gets updated from time to time in case root certs expire? – BillF Aug 04 '11 at 04:05
  • Yes, MT relies on iOS to know what's trusted. Root certificate generally have very long lifes so expiration is rarely an issue. However updates often occurs so more roots can be added. – poupou Aug 04 '11 at 14:09
2

MonoTouch (just like Mono) does not support TLS_DH* cipher suites (like TLS_DHE_DSS_WITH_AES_128_CBC_SHA).

When a server is configured to accept only them then the negotiation stage fails very early (an Alert is received from the server after the Client Hello message is sent) which explains why the callback was never called.

Ensure your server allows the more traditional cipher suites, e.g. the very secure (but slow) TLS_RSA_WITH_AES_256_CBC_SHA or the faster (and very common) Cipher Suite: TLS_RSA_WITH_RC4_128_[MD5|SHA], and Mono[Touch] should work well using them.

Note that this is unrelated to SOAP or web-services (and even X.509 certificates) - it's just plain SSL.

poupou
  • 43,413
  • 6
  • 77
  • 174
  • Pursuing this great suggestion and will get back to confirm success. Really appreciate your help. BTW this was reported as bug 170 at bugzilla.xamarin.com. – BillF Aug 07 '11 at 15:09
  • Am a bit lost now because our tech support says TLS_RSA_WITH_AES_256_CBC_SHA is already supported on our server. Any ideas? His report is below. – BillF Aug 24 '11 at 05:29
  • Tech support report seems to be too long to include. It says our Tomcat is using the Sun JSSE extension for SSL and all supported Sun JSSE “Ciphers” are currently enabled for Tomcat. The list of 18 ciphers includes TLS_RSA_WITH_AES_256_CBC_SHA. – BillF Aug 24 '11 at 05:35
  • 2
    The supported ciphers: SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA SSL_DHE_DSS_WITH_DES_CBC_SHA SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA SSL_DHE_RSA_WITH_DES_CBC_SHA SSL_RSA_EXPORT_WITH_DES40_CBC_SHA SSL_RSA_EXPORT_WITH_RC4_40_MD5 SSL_RSA_WITH_3DES_EDE_CBC_SHA SSL_RSA_WITH_DES_CBC_SHA SSL_RSA_WITH_RC4_128_MD5 SSL_RSA_WITH_RC4_128_SHA TLS_DHE_DSS_WITH_AES_128_CBC_SHA TLS_DHE_DSS_WITH_AES_256_CBC_SHA TLS_DHE_RSA_WITH_AES_128_CBC_SHA TLS_DHE_RSA_WITH_AES_256_CBC_SHA TLS_RSA_WITH_AES_128_CBC_SHA TLS_RSA_WITH_AES_256_CBC_SHA – BillF Aug 24 '11 at 05:56
  • SSL/TLS protocol (see RFCs) makes the "final" decision about ciphers a _server_ decision. This decision _should_ be based on the list of ciphers that the client sends to the server. Mono does _not_ send any TLS_DHE_* ciphers to the server (since it does not support them) but wireshark traces shows that the server still select it (as it shows that the client did not send that choice). Sorry but I don't see (or know of) anything, outside the server, that could end up in this situation. – poupou Aug 24 '11 at 12:11
  • Final resolution: Replacing the ancient self-signed cert on the server with a Verisign cert solved the problem.Thanks to @poupou – BillF Oct 25 '11 at 12:13
  • Weird, maybe the old certificate was not using an RSA public key ? otherwise it should have worked. Anyway I'm glad you found out how to solve your issue! – poupou Oct 25 '11 at 12:18
  • Yes the self-signed cert was DSA. In any case it was several years old and so I imagine was not supporting modern TLS. – BillF Oct 25 '11 at 22:26
  • @ poupou Moral for those accessing TIBCO BusinessWorks services - don't use the self-signed DSA SSL cert that TIBCO provides for Tomcat. – BillF Oct 25 '11 at 22:30
  • Ouch, sorry I never though of that :( I'm still surprised that the list of supported ciphers included all the TLS_RSA_* ... anyway lesson learned :) – poupou Oct 26 '11 at 00:06