2011-02-07 19 views
62

estoy haciendo un cliente REST fácil de usar en mis aplicaciones C#. En .net en Windows Funciona muy bien con http: // y https: // connections. En mono 2.6.7 (también probado con 2.8 con los mismos resultados) en Ubuntu 10.10 solo funciona http: //. https: // conexiones vomitan esta excepción en el método request.GetResponse():Mono https WebRequest falla con "La autentificación o descifrado ha fallado"

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 

No he podido encontrar ninguna manera de solucionar este problema. ¿Alguien tiene alguna idea de por qué está sucediendo esto y cómo solucionarlo?

Una vez más, esto sólo fracasa en Mono, .NET no parece tener ningún problema de establecer una conexión.

aquí está el código de llamada:

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

¿Ha intentado ejecutar en Windows/Mono, Windows/.NET? – abatishchev

+0

Como se explica en mi pregunta, lo probé en Windows/.NET, sin embargo, nunca lo intenté en Mono en Windows. Sin embargo, sería interesante ver si funciona. Sin embargo, la publicación de jpobst parece ser la solución a mi problema. – Joel

+4

Arrastrando a los muertos aquí - ¿me estoy perdiendo algo? ¿Dónde está la publicación de jpobst? –

Respuesta

26

El .NET Framework en Windows utiliza el almacén de certificados de Windows (MMC, Agregar/quitar complementos, certificados) para determinar si se acepta un certificado SSL de un mando a distancia sitio. Windows se envía con un montón de Root y Autoridades de certificación intermedias (CA) y Windows Update las actualiza periódicamente. Como resultado, su código .NET generalmente confiará en un certificado siempre que haya sido emitido por una CA o un descendiente de una CA en el almacén de certificados (se incluyen las CA comerciales más acreditadas).

en mono, no hay ningún almacén de certificados de Windows. Mono tiene su propia tienda. Por defecto, está vacío (no hay CA predeterminadas de confianza). Necesita administrar las entradas usted mismo.

Echa un vistazo aquí:

El punto mozroots.exe hará que su mono instalar a confiar en todo lo que confía en Firefox después de una instalación predeterminada.

+1

Una nota rápida aquí sobre el certificado raíz G2 de Entrust, que actualmente NO está incluido en la tienda CA de Mozilla. Están planeando agregarlo para el lanzamiento de Firefox 38, pero eso no está garantizado. Actualmente, si usa la tienda de CA de Mozilla, su código no podrá verificar los certificados que están firmados con el certificado raíz de G2. (Las huellas digitales están en http://www.entrust.net/developer/, el error de Mozilla de ** 2013 ** está en https://bugzilla.mozilla.org/show_bug.cgi?id=849950) –

7

Escriba esta línea antes de realizar la solicitud http request. esto debería ser trabajo.

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

Considere incluir algunas información sobre su respuesta, en lugar de simplemente publicar el código. Intentamos proporcionar no solo 'soluciones', sino también ayudar a las personas a aprender. Debe explicar qué sucedió en el código original, qué hizo de manera diferente y por qué funcionó su (s) cambio (s). –

+3

Esto es contrario a la seguridad web razonable: simplemente acepta cualquier certificado sin antes validarlo. Vea la respuesta de @Ludovic para una función que valida antes de aceptar. – ssamuel

3

Encuentro el error también.

intenté ServicePointManager.ServerCertificateValidationCallback y ServicePointManager.CertificatePolicy pero todavía no funcionó.

I anger. construir un wraper cURL. Funciona bien para mi proyecto de juguete.

/// <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

Tuve el mismo problema. Cuando la respuesta http lanza esta excepción entonces yo:

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

Esto importa los certificados que faltan y la excepción no sucedió de nuevo.

2

La primera respuesta dice que ya: Mono nada que no sea Windows no viene con nada por lo que inicialmente no confía en ningún certificado. ¿Entonces lo que hay que hacer?

Aquí es un buen artículo sobre diferentes maneras de lidiar con el problema desde la perspectiva del desarrollador: http://www.mono-project.com/archived/usingtrustedrootsrespectfully/

Breve resumen: Puede:

  • ignoran el problema de seguridad
  • ignoran el problema
  • informa al usuario y cancela
  • deja que el usuario saber y darle una opción para continuar bajo su propio riesgo

El enlace de arriba viene con ejemplos de código para cada caso.

+1

llamadas a mozroots --import --sync debería arreglarlo agregando los certificados predeterminados de mozilla en el almacén de certificados mono. – ScottB

42

Tuve el mismo problema con Unity (que también usa mono) y this post me ayudó a resolverlo.

Apenas añada la siguiente línea antes de hacer su solicitud:

ServicePointManager.ServerCertificateValidationCallback = MyRemoteCertificateValidationCallback; 

Y este método:

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

Esto debería funcionar de hecho, pero en mi cliente 'swagger' generado automáticamente parece que no. Consulte https://github.com/swagger-api/swagger-editor/issues/1034 – loretoparisi

+2

Funcionó como el encanto. (Y) –

+0

Existe un problema potencial que 'cadena.Build (cert) 'siempre devuelve true en solicitud mono uniforme a un [bad ssl] (https://badssl.com). – sakiM

2

Otra solución para la unidad es para inicializar el ServicePointManager una vez para aceptar siempre los certificados. Esto funciona, pero obviamente no es seguro.

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

Puede simplificar esta respuesta en una línea: 'ServicePointManager.ServerCertificateValidationCallback + = (p1, p2, p3, p4) => true;' – Andy

Cuestiones relacionadas