2017-10-04 14 views
8

Próbuję użyć certyfikatów klienta do uwierzytelniania w aplikacji Xamarin Forms dla systemu iOS, ale nic nie działa. Jeśli uruchomię żądanie, aplikacja czeka na domyślny limit czasu wynoszący 100 sekund (próbowałem obniżyć go za pomocą HttpWebRequest.Timeout, ale ustawienie wydaje się być ignorowane), po czym otrzymuję następujący wyjątek: Błąd otrzymania strumienia odpowiedzi (ReadDone1): ReceiveFailureFormularze Xamarin https z certyfikatami klienta zgłasza wyjątek WebException

Ten sam kod, co aplikacja konsoli systemu Windows, działa poprawnie. Oto niektóre (uproszczone dla czytelności) fragmenty do odtworzenia:

Klient:

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

Serwer: 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> 

Jest to wyjątek ciśgle:

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 i Mac Xamarin SDK 11.0.0.0
  • Wdrożenie docelowa iOS 9.3 (10,3 doesnt pracy, jak również)
  • Windows Server 2008 R2 Enterprise SP1
  • IIS 7

dodałem sprawdzanie certyfikatu serwera (który jest podpisany przez naszą domeną-CA) w AppDelegate.cs, zgłasza 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) 
} 

Żądanie działa, jeśli wyłączę wymaganie dotyczące certyfikatu na serwerze sieci Web, aby połączenie działało.

Próbowałem nawet System.Net.HttpClient, ale rzuca NotImplementedException podczas dostępu do Certyfikat klienta obiekt :(

Teraz jestem kompletnie pojęcia co próbować dalej ... wszystkie inne pytania Znalazłem było kilka lat, oraz nie oferuje żadnych rozwiązań pracujących

Odpowiedz

2

W swojej obsługi wyjątków, wygląda na to, że nie wzywają Response.End():

 catch (Exception ex) 
     { 
      if (!(ex is System.Threading.ThreadAbortException)) 
       Response.Write(ex.ToString()); 
     } 

Jeśli więc zgłaszany jest wyjątek po stronie serwera, możliwe, że odpowiedź nigdy nie zostanie wysłana do klienta. Poleciłbym dodać trochę logowania po stronie serwera wyjątku i zobaczyć, czy coś użytecznego jest zapewnione w ten sposób.

Wygląda na to, że weryfikujesz niektóre pola certyfikatu klienta, ale na tyle, na ile mogę powiedzieć, nie sprawdzaj, czy certyfikat klienta jest podpisany przez zaufanego użytkownika root. Powinieneś upewnić się, że tak jest, lub każdy może wygenerować samopodpisany certyfikat zawierający nazwę użytkownika, którą chce podszyć się pod niego, i użyć go do oszukania twojego kodu, co byłoby poważną luką w zabezpieczeniach.

+0

Odpowiedź Response.End nie jest rzeczywiście potrzebna do odpowiedzi na koniec, po prostu chciałem, aby serwer nie dodał html do odpowiedzi. Serwer nie powinien być problemem, ponieważ kod klienta działa jako aplikacja konsolowa, ale nie w Xamarin iOS. Nadal będę to sprawdzać w poniedziałek, nie mogę uzyskać dostępu do mojej pracy z domu. Brakuje certyfikatu, dziękuję za poradę – Brandtware

+0

Dodałem kolejną odpowiedź. Do bloku catch, nic nie zmieniłem. Sprawdziłem dzienniki IIS, wydaje się, że żądanie nie trafiło nawet na serwer, wygląda jeszcze bardziej na problem z klientem ... – Brandtware

1

Znalazłem kilka pytań, które wskazują na problem ze stosem HTTP Mono.Może pomóc w rozwiązaniu problemu: How to validate SSL certificates when using HttpWebRequest, Urgent help needed: "error getting response stream (ReadDone1): ReceiveFailure". Jednym z możliwych sposobów jest użycie ModernHttpClient, które używa platformy natywnego klienta HTTP.

Również masz błąd w oddzwonieniu w ServicePointManager.ServerCertificateValidationCallback property. RemoteCertificateValidationCallback delegate zwraca wartość logiczną, która określa, czy określony certyfikat jest akceptowany do uwierzytelniania. Zobacz więcej szczegółów w przykładach MSDN. Spróbuj także przeczytać pytanie: How to validate SSL certificates when using HttpWebRequest.

+0

Znalazłem kilka wątków o ModernHttpClient, problem polega tylko na ustawianiu rodzimych rzeczy i używaniu normalny HttpClient pod spodem. Jak wspomniano w pytaniu, HttpClient.ClientCertificate zgłasza wyjątek NotImplementedException. Przypadkowo usunąłem wartość zwracania zwrotnego w oczyszczaniu kodu w celu opublikowania, nie skompilowałoby się bez niej. Próbowałem zarówno zwracać true (niezalecane ze względów bezpieczeństwa) i zwracać sslPolicyErrors == SslPolicyError.None, żaden z nich nie działa. Odpowiem odpowiednio na pytanie – Brandtware

+0

W tym przypadku spróbuj sprawdzić poprawność twojego certyfikatu po stronie serwera i klienta. Upewnij się, że masz dostęp do pełnego certyfikatu, w tym klucza prywatnego. Certyfikat powinien znajdować się w magazynie certyfikatów Zaufane główne urzędy certyfikacji dla serwera WWW. Przypuszczam, że [to pytanie] (https://stackoverflow.com/questions/35582396/how-to-use-a-client-certificate-to-authenticate-and-authorize-in-a-web-api) może ci pomóc . – Didgeridoo

+0

ClientCertificate.IsValid i HasPrivateKey są prawdziwe. Główny urząd certyfikacji istnieje na urządzeniu (Ustawienia -> Ogólne -> Profile -> (nasza nazwa MDM) -> więcej szczegółów -> nasz certyfikat główny) oraz w zaufanych źródłach na serwerze internetowym. Ten sam plik certyfikatu może być użyty w aplikacji konsolowej, więc powinien działać. – Brandtware

1

Co się stanie, jeśli połączysz się z serwerem przez HTTPS bez dodawania certyfikatu?

Czy żądanie trafia na serwer w porządku? Jeśli tak, problem prawdopodobnie dotyczy dekodowania zaszyfrowanej odpowiedzi. Czy certyfikaty na kliencie i serwerze są takie same?

+0

Interesujące, jeśli zadzwonię do https: // nazwaserwera/bez certyfikatu, to zadziała, jeśli zadzwonię do https: //servername/test/test.aspx, to nie zadziała (nawet po dezaktywacji "wymaganego certyfikatu klienta" w ustawieniach ssl). .. Będę testować nieco więcej szukając wzoru – Brandtware

Powiązane problemy