通过 SSL 的 MonoTouch Web 服务请求获取“身份验证或解密失败”

发布于 2024-11-27 17:11:20 字数 5295 浏览 1 评论 0原文

通过 SSL 的 Web 服务请求在 Monotouch v4.0.4.1 上引发 WebException:

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

由于服务器的 SSL 证书是自签名的(顺便说一句,我认为它不是 X.509),因此我使用 ServicePointManager.ServerCertificateValidationCallback 绕过了证书验证。完全相同的代码在 Windows .NET 上运行良好,其中 Web 服务调用返回正确的结果。在 Monotouch 上添加 Writeline 显示永远不会到达 ServerCertificateValidationCallback 委托代码。

注意:虽然可能不相关,但请求的内容是带有嵌入式 WS-Security UsernameToken 的 SOAP。

  1. 有人在 MonoTouch 上有类似的东西吗?见过类似症状的报告,但没有解决。代码和堆栈跟踪如下,欢迎任何评论。如果需要的话,可以通过电子邮件发送一个独立的测试用例。

  2. 我收集到有一种替代方法,使用 certmgr.exe 将自签名服务器证书存储在本地信任存储中,但似乎无法在 MonoTouch 发行版中找到该应用程序。有人能指点我吗?

..

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);
        }
    }
} 

堆栈跟踪(为了保护无辜者而更改了一些名称,可根据要求提供原始版本):

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

活雷疯 2024-12-04 17:11:20

MonoTouch(就像 Mono)不支持 TLS_DH* 密码套件(例如 TLS_DHE_DSS_WITH_AES_128_CBC_SHA)。

当服务器配置为仅接受它们时,协商阶段会很早就失败(发送客户端 Hello 消息后从服务器收到警报),这解释了为什么从未调用回调。

确保您的服务器允许更传统的密码套件,例如非常安全(但速度较慢)的 TLS_RSA_WITH_AES_256_CBC_SHA 或更快(且非常常见)的密码套件:TLS_RSA_WITH_RC4_128_[MD5|SHA],并且 Mono[Touch] 应该可以很好地使用它们。

请注意,这与 SOAP 或 Web 服务(甚至 X.509 证书)无关 - 它只是普通的 SSL。

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.

孤独岁月 2024-12-04 17:11:20

1) 不受信任的根证书并不是可能导致此异常的唯一问题。

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

在那里添加一个 Console.WriteLine ,这样您就可以看到它是否被调用(或没有)。

throw new WebException(e.Message);

这里还有另一个,具有完整的堆栈跟踪(不仅仅是 Message 属性)。

2) 每个应用程序都是隔离的。这意味着:

  • 应用程序无法更新全局 iOS 证书存储(这会产生安全问题);

  • (对于 MT),它只能使用仅对其自身可用的本地(单声道)存储(这对您自己的应用程序没有任何帮助)

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)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文