2011-02-07 9 views
62

Sto facendo un semplice client REST da utilizzare nelle mie applicazioni C#. In .net su Windows Funziona alla grande con le connessioni http: // e https: //. In mono 2.6.7 (testato anche con 2.8 con gli stessi risultati) su Ubuntu 10.10 solo http: // funziona. https: // connessioni vomitare questa eccezione sul metodo request.GetResponse():Mono https webrequest non riesce con "L'autenticazione o la decrittografia non è riuscita"

Unhandled Exception: System.Net.WebException: Error getting response stream (Write: The authentication or decryption has failed.): SendFailure ---> System.IO.IOException: The authentication or decryption has failed. ---> Mono.Security.Protocol.Tls.TlsException: Invalid certificate received from server. Error code: 0xffffffff800b010a 
    at Mono.Security.Protocol.Tls.Handshake.Client.TlsServerCertificate.validateCertificates (Mono.Security.X509.X509CertificateCollection certificates) [0x00000] in <filename unknown>:0 
    at Mono.Security.Protocol.Tls.Handshake.Client.TlsServerCertificate.ProcessAsTls1() [0x00000] in <filename unknown>:0 
    at Mono.Security.Protocol.Tls.Handshake.HandshakeMessage.Process() [0x00000] in <filename unknown>:0 
    at (wrapper remoting-invoke-with-check) Mono.Security.Protocol.Tls.Handshake.HandshakeMessage:Process() 
    at Mono.Security.Protocol.Tls.ClientRecordProtocol.ProcessHandshakeMessage (Mono.Security.Protocol.Tls.TlsStream handMsg) [0x00000] in <filename unknown>:0 
    at Mono.Security.Protocol.Tls.RecordProtocol.InternalReceiveRecordCallback (IAsyncResult asyncResult) [0x00000] in <filename unknown>:0 
    --- End of inner exception stack trace --- 
    at Mono.Security.Protocol.Tls.SslStreamBase.AsyncHandshakeCallback (IAsyncResult asyncResult) [0x00000] in <filename unknown>:0 
    --- End of inner exception stack trace --- 
    at System.Net.HttpWebRequest.EndGetResponse (IAsyncResult asyncResult) [0x00000] in <filename unknown>:0 
    at System.Net.HttpWebRequest.GetResponse() [0x00000] in <filename unknown>:0 

non sono stato in grado di trovare un modo per risolvere questo problema. Qualcuno ha idea del perché questo sta accadendo e come risolverlo?

Anche in questo caso, solo in Mono, .Net non sembra avere problemi a stabilire una connessione.

ecco il codice chiamante:

public JToken DoRequest(string path, params string[] parameters) { 
    if(!path.StartsWith("/")) { 
     path = "/" + path; 
    } 
    string fullUrl = url + path + ToQueryString(parameters); 

    if(DebugUrls) Console.WriteLine("Requesting: {0}", fullUrl); 

    WebRequest request = HttpWebRequest.CreateDefault(new Uri(fullUrl)); 
    using(WebResponse response = request.GetResponse()) 
    using(Stream responseStream = response.GetResponseStream()) { 
     return ReadResponse(responseStream); 
    } 
} 
+0

Hai provato a eseguire in Windows/Mono, Windows/.NET? – abatishchev

+0

Come spiegato nella mia domanda, l'ho provato su Windows/.NET, ma non l'ho mai provato su Mono su Windows. Sarebbe interessante vedere se funziona comunque. Tuttavia, il post di jpobst sembra essere la soluzione al mio problema. – Joel

+4

Trascinare qui i morti - mi manca qualcosa? dov'è il post di jpobst? –

risposta

26

.NET Framework su Windows utilizza l'archivio certificati di Windows (mmc, Aggiungi/Rimuovi snap-in, certificati) per determinare se accettare un certificato SSL da un telecomando luogo. Windows viene fornito con un gruppo di autorità di certificazione radice e intermedie (CA) e viene aggiornato periodicamente da Windows Update. Di conseguenza, il codice .NET generalmente si fida di un certificato a condizione che sia stato emesso da una CA o da un discendente di una CA nell'archivio certificati (sono incluse le CA commerciali più affidabili).

In Mono, non è presente un archivio di certificati Windows. Mono ha il suo negozio. Per impostazione predefinita, è vuoto (non ci sono CA predefinite che sono attendibili). È necessario gestire le voci da soli.

dare uno sguardo qui:

Il punto mozroots.exe causerà il vostro mono installare a fidarsi di tutto ciò che si fida di Firefox dopo una installazione di default.

+1

Una breve nota qui sul certificato radice G2 di Entrust, che attualmente NON è incluso nell'archivio CA di Mozilla. Stanno progettando di averlo aggiunto per il rilascio di Firefox 38, ma non è garantito. Attualmente, se si utilizza l'archivio CA di Mozilla, il codice non riuscirà a verificare i certificati firmati con il certificato radice G2. (I thumbprint sono disponibili all'indirizzo http://www.entrust.net/developer/, il bug di Mozilla da ** 2013 ** si trova su https://bugzilla.mozilla.org/show_bug.cgi?id=849950) –

7

Scrivere questa riga prima di richiedere la richiesta http. questo dovrebbe essere lavoro.

ServicePointManager.ServerCertificateValidationCallback += new RemoteCertificateValidationCallback((sender, certificate, chain, policyErrors) => { return true; }); 


private static bool RemoteCertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) 
    { 
     //Return true if the server certificate is ok 
     if (sslPolicyErrors == SslPolicyErrors.None) 
      return true; 

     bool acceptCertificate = true; 
     string msg = "The server could not be validated for the following reason(s):\r\n"; 

     //The server did not present a certificate 
     if ((sslPolicyErrors & 
      SslPolicyErrors.RemoteCertificateNotAvailable) == SslPolicyErrors.RemoteCertificateNotAvailable) 
     { 
      msg = msg + "\r\n -The server did not present a certificate.\r\n"; 
      acceptCertificate = false; 
     } 
     else 
     { 
      //The certificate does not match the server name 
      if ((sslPolicyErrors & 
       SslPolicyErrors.RemoteCertificateNameMismatch) == SslPolicyErrors.RemoteCertificateNameMismatch) 
      { 
       msg = msg + "\r\n -The certificate name does not match the authenticated name.\r\n"; 
       acceptCertificate = false; 
      } 

      //There is some other problem with the certificate 
      if ((sslPolicyErrors & 
       SslPolicyErrors.RemoteCertificateChainErrors) == SslPolicyErrors.RemoteCertificateChainErrors) 
      { 
       foreach (X509ChainStatus item in chain.ChainStatus) 
       { 
        if (item.Status != X509ChainStatusFlags.RevocationStatusUnknown && 
         item.Status != X509ChainStatusFlags.OfflineRevocation) 
         break; 

        if (item.Status != X509ChainStatusFlags.NoError) 
        { 
         msg = msg + "\r\n -" + item.StatusInformation; 
         acceptCertificate = false; 
        } 
       } 
      } 
     } 

     //If Validation failed, present message box 
     if (acceptCertificate == false) 
     { 
      msg = msg + "\r\nDo you wish to override the security check?"; 
//   if (MessageBox.Show(msg, "Security Alert: Server could not be validated", 
//      MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1) == DialogResult.Yes) 
       acceptCertificate = true; 
     } 

     return acceptCertificate; 
    } 
+6

Si prega di considerare di includere alcuni informazioni sulla tua risposta, piuttosto che semplicemente postare il codice. Cerchiamo di fornire non solo "correzioni", ma aiutare le persone a imparare. Dovresti spiegare cosa c'era di sbagliato nel codice originale, cosa hai fatto in modo diverso e perché le tue modifiche hanno funzionato. –

+3

Questo è contrario alla ragionevole sicurezza del web: stai semplicemente accettando qualsiasi certificato senza prima averlo convalidato. Vedere la risposta di @Ludovic per una funzione che convalida prima di accettare. – ssamuel

3

ho incontrato l'errore troppo.

Ho provato ServicePointManager.ServerCertificateValidationCallback e ServicePointManager.CertificatePolicy ma ancora non funziona.

I rabbia. costruire un wraper arricciato. Funziona bene per il mio progetto di giocattolo.

/// <summary> 
/// For MONO ssl decryption failed 
/// </summary> 
public static string PostString(string url, string data) 
{ 
    Process p = null; 
    try 
    { 
     var psi = new ProcessStartInfo 
     { 
      FileName = "curl", 
      Arguments = string.Format("-k {0} --data \"{1}\"", url, data), 
      RedirectStandardOutput = true, 
      UseShellExecute = false, 
      CreateNoWindow = false, 
     }; 

     p = Process.Start(psi); 

     return p.StandardOutput.ReadToEnd(); 
    } 
    finally 
    { 
     if (p != null && p.HasExited == false) 
      p.Kill(); 
    } 
} 
3

Ho avuto lo stesso problema. Quando la risposta http genera questa eccezione poi faccio:

System.Diagnostics.Process.Start("mozroots","--import --quiet"); 

questo importa i certificati mancanti e l'eccezione non happend di nuovo.

2

La prima risposta dice già: Mono su qualsiasi cosa diversa da Windows non viene fornito con nulla così inizialmente non si fida di alcun certificato. Quindi che si fa?

Ecco un bell'articolo sui diversi modi per affrontare il problema dal punto di vista dello sviluppatore: http://www.mono-project.com/archived/usingtrustedrootsrespectfully/

Breve riassunto: È possibile:

  • ignorare il problema di sicurezza
  • ignorare il problema
  • consente all'utente di conoscere e interrompere
  • lasciare che l'utente conoscere e dargli una scelta di continuare a proprio rischio

Il collegamento precedente viene fornito con esempi di codice per ogni caso.

+1

chiama mozroots --import --sync dovrebbe aggiustarlo per te aggiungendo i certificati predefiniti da mozilla nell'archivio certificati mono. – ScottB

42

Ho avuto lo stesso problema con Unity (che utilizza anche mono) e this post mi ha aiutato a risolverlo.

Basta aggiungere la seguente riga prima di effettuare la vostra richiesta:

ServicePointManager.ServerCertificateValidationCallback = MyRemoteCertificateValidationCallback; 

E questo metodo:

public bool MyRemoteCertificateValidationCallback(System.Object sender, 
    X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) 
{ 
    bool isOk = true; 
    // If there are errors in the certificate chain, 
    // look at each error to determine the cause. 
    if (sslPolicyErrors != SslPolicyErrors.None) { 
     for (int i=0; i<chain.ChainStatus.Length; i++) { 
      if (chain.ChainStatus[i].Status == X509ChainStatusFlags.RevocationStatusUnknown) { 
       continue; 
      } 
      chain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain; 
      chain.ChainPolicy.RevocationMode = X509RevocationMode.Online; 
      chain.ChainPolicy.UrlRetrievalTimeout = new TimeSpan (0, 1, 0); 
      chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllFlags; 
      bool chainIsValid = chain.Build ((X509Certificate2)certificate); 
      if (!chainIsValid) { 
       isOk = false; 
       break; 
      } 
     } 
    } 
    return isOk; 
} 
+0

Questo dovrebbe funzionare di fatto, ma nel mio client '' swagger' generato automaticamente non sembra. Vedere https://github.com/swagger-api/swagger-editor/issues/1034 – loretoparisi

+2

ha funzionato come il fascino. (Y) –

+0

C'è un potenziale problema che 'catena.Build (cert) 'restituisce sempre true in mono anche a richiesta su [bad ssl] (https://badssl.com). – sakiM

2

Un'altra soluzione per l'unità è per inizializzare il ServicePointManager una volta per accettare sempre i certificati. Questo funziona, ma ovviamente non è sicuro.

System.Net.ServicePointManager.ServerCertificateValidationCallback += 
      delegate (object sender, System.Security.Cryptography.X509Certificates.X509Certificate certificate, 
            System.Security.Cryptography.X509Certificates.X509Chain chain, 
            System.Net.Security.SslPolicyErrors sslPolicyErrors) 
      { 
       return true; // **** Always accept 
     }; 
+3

È possibile semplificare questa risposta a una riga: 'ServicePointManager.ServerCertificateValidationCallback + = (p1, p2, p3, p4) => true;' – Andy

Problemi correlati