I am trying to use client certificates for authentication in a Xamarin Forms App for iOS, but nothing seems to work. If I start the request the app waits to the default timeout of 100 seconds (I tried lowering it with HttpWebRequest.Timeout, but setting it seems to be ignored), after that, I get the following exception: Error getting response stream (ReadDone1): ReceiveFailure
The same code as a Windows console application works fine. Here some (simplified for readability) snippets to reproduce:
Client:
var clientCertificate = new X509Certificate2(data, pwd); var result = await ExecuteRequest("https://server/user.aspx", clientCertificate); public static async Task<string> ExecuteRequest(Uri uri, X509Certificate2 clientCertificate) { var hwr = (HttpWebRequest)HttpWebRequest.Create(uri); if (clientCertificate != null) { hwr.ClientCertificates.Add(clientCertificate); } hwr.Method = "GET"; try { var resWebResonse = await hwr.GetResponseAsync(); var stream = resWebResonse.GetResponseStream(); var sr = new StreamReader (stream); return sr.ReadToEnd(); } catch (Exception ex) { Debug.WriteLine($"Error executing Webrequest to {uri}: {ex}"); } return null; } Server: user.aspx
<%@ Page Language="C#" %> <%@ Import Namespace="System.Security.Cryptography.X509Certificates" %> <%@ Import Namespace="System.Security.Principal" %> <script runat="server"> public void Page_Load(object sender, EventArgs e) { try { Regex userRegEx = new Regex("CN=(.*)@", RegexOptions.IgnoreCase | RegexOptions.Compiled); if (Request.ClientCertificate.IsPresent) { var cert = new X509Certificate2(Request.ClientCertificate.Certificate); var identity = new GenericIdentity(cert.Subject, "ClientCertificate"); var principal = new GenericPrincipal(identity, null); var m = userRegEx.Match (principal.Identity.Name); Response.Write (m.Groups[1].Value); } else Response.Write("No Client-Certificate"); Response.End(); } catch (Exception ex) { if (!(ex is System.Threading.ThreadAbortException)) Response.Write(ex.ToString()); } } </script> This is the exception I keep getting:
System.Net.WebException: Error getting response stream (ReadDone1): ReceiveFailure ---> System.IO.IOException: Unable to read data from the transport connection: Connection reset by peer. ---> System.Net.Sockets.SocketException: Connection reset by peer at System.Net.Sockets.Socket.Receive (System.Byte[] buffer, System.Int32 offset, System.Int32 size, System.Net.Sockets.SocketFlags socketFlags) [0x00017] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.0.0.0/src/mono/mcs/class/referencesource/System/net/System/Net/Sockets/Socket.cs:1773 at System.Net.Sockets.NetworkStream.Read (System.Byte[] buffer, System.Int32 offset, System.Int32 size) [0x0009b] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.0.0.0/src/mono/mcs/class/referencesource/System/net/System/Net/Sockets/NetworkStream.cs:513 --- End of inner exception stack trace --- at Mono.Net.Security.MobileAuthenticatedStream.EndReadOrWrite (System.IAsyncResult asyncResult, Mono.Net.Security.AsyncProtocolRequest& nestedRequest) [0x00056] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.0.0.0/src/mono/mcs/class/System/Mono.Net.Security/MobileAuthenticatedStream.cs:335 at Mono.Net.Security.MobileAuthenticatedStream.EndRead (System.IAsyncResult asyncResult) [0x00000] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.0.0.0/src/mono/mcs/class/System/Mono.Net.Security/MobileAuthenticatedStream.cs:278 at System.Net.WebConnection.ReadDone (System.IAsyncResult result) [0x00027] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.0.0.0/src/mono/mcs/class/System/System.Net/WebConnection.cs:475 --- End of inner exception stack trace --- at System.Net.HttpWebRequest.EndGetResponse (System.IAsyncResult asyncResult) [0x00059] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.0.0.0/src/mono/mcs/class/System/System.Net/HttpWebRequest.cs:1031 at System.Threading.Tasks.TaskFactory`1[TResult].FromAsyncCoreLogic (System.IAsyncResult iar, System.Func`2[T,TResult] endFunction, System.Action`1[T] endAction, System.Threading.Tasks.Task`1[TResult] promise, System.Boolean requiresSynchronization) [0x0000f] in <6314851f133e4e74a2e96356deaa0c6c>:0 --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x0000c] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.0.0.0/src/mono/mcs/class/referencesource/mscorlib/system/runtime/exceptionservices/exceptionservicescommon.cs:151 at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess (System.Threading.Tasks.Task task) [0x00037] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.0.0.0/src/mono/mcs/class/referencesource/mscorlib/system/runtime/compilerservices/TaskAwaiter.cs:187 at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (System.Threading.Tasks.Task task) [0x00028] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.0.0.0/src/mono/mcs/class/referencesource/mscorlib/system/runtime/compilerservices/TaskAwaiter.cs:156 at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd (System.Threading.Tasks.Task task) [0x00008] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.0.0.0/src/mono/mcs/class/referencesource/mscorlib/system/runtime/compilerservices/TaskAwaiter.cs:128 at System.Runtime.CompilerServices.TaskAwaiter`1[TResult].GetResult () [0x00000] in <6314851f133e4e74a2e96356deaa0c6c>:0 at EWOIS.iOS.WebHelper+<ExecuteRequest>d__4.MoveNext () [0x0012e] in C:\xxxx\WebHelper.cs:70 - Visual Studio 2017 v15.3.5
- Xamarin 4.7.9.45
- Xamarin iOS and Xamarin Mac SDK 11.0.0.0
- Deployment Target iOS 9.3 (10.3 doesn´t work as well)
- Windows Server 2008 R2 Enterprise SP1
- IIS 7
I added a check for the server certificate (which is signed by our domain-CA) in AppDelegate.cs, it reports SslPolicyErrors.None
System.Net.ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => { if (cert != null) System.Diagnostics.Debug.WriteLine($"Servercertificate: {cert.Subject}, SSL-Policy-Errors: {sslPolicyErrors}"); return sslPolicyErrors == SslPolicyErrors.None; // return true does not work either (and is not recommended for security reasons) } The request works if I disable the certificate requirement on the web server, so the connection is working.
I even tried System.Net.HttpClient, but it throws a NotImplementedException when accessing the ClientCertificate Property :(
Now I am completely clueless what to try next... all other questions I found were a few years old and don´t offer any working solutions