2011-02-07 1 views
62

Я делаю простой клиент REST для использования в моих приложениях C#. В .net на Windows Он отлично работает с http: // и https: // соединениями. В моно 2.6.7 (также проверено с 2.8 с теми же результатами) на Ubuntu 10.10 работает только http: //. https: // соединения подбросить это исключение на методе request.GetResponse():Моно https webrequest завершилось с ошибкой «Ошибка аутентификации или дешифрования»

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 

Я не смог найти какой-либо способ, чтобы исправить это. Кто-нибудь знает, почему это происходит и как это исправить?

Опять же, это только не удается в Mono .Net, похоже, не имеет проблем с установлением соединения.

вот код вызова:

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

Вы пытались запустить Windows/Mono, Windows/.NET? – abatishchev

+0

Как объяснялось в моем вопросе, я попробовал его в Windows/.NET, однако я никогда не пробовал его в Mono в Windows. Было бы интересно посмотреть, работает ли это. Тем не менее, сообщение jpobst, похоже, является решением моей проблемы. – Joel

+4

Перетаскивание мертвых здесь - я что-то упускаю? где почта jpobst? –

ответ

26

В .NET Framework на Windows, использует хранилище Windows, сертификаты (ММС, Добавить/Удалить оснастку Ins, сертификаты), чтобы определить, принимать ли сертификат SSL от удаленного сайт. Windows поставляется с кучей корневых и промежуточных центров сертификации (CA), и они периодически обновляются с помощью Центра обновления Windows. В результате ваш код .NET, как правило, будет доверять сертификату, если он был выпущен CA или потомок CA в хранилище сертификатов (включены наиболее авторитетные коммерческие ЦС).

В Mono нет хранилища сертификатов Windows. У Моно есть собственный магазин. По умолчанию он пуст (нет доверенных ЦС по умолчанию). Вам нужно управлять своими записями самостоятельно.

Посмотрите здесь:

Точка mozroots.exe заставит ваш моно установить доверять всем, что Firefox доверяет после установки по умолчанию.

+1

Обратите внимание на судовой сертификат Entrust's G2, который в настоящее время НЕ входит в хранилище CA Mozilla. Они планируют добавить его для выпуска Firefox 38, но это не гарантировано. В настоящее время, если вы используете хранилище CA Mozilla, ваш код не сможет проверить сертификаты, подписанные с корневым сертификатом G2. (Отпечатки находятся по адресу http://www.entrust.net/developer/, ошибка Mozilla с ** 2013 ** находится по адресу https://bugzilla.mozilla.org/show_bug.cgi?id=849950) –

7

Запишите эту строку перед запросом HTTP-запроса. это должна быть работа.

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

Пожалуйста, включите некоторые информацию о вашем ответе, а не просто отправку кода. Мы стараемся предоставлять не только «исправления», но и помогать людям учиться. Вы должны объяснить, что было не так в исходном коде, что вы сделали по-другому и почему ваши изменения (ы) работали. –

+3

Это противоречит разумной безопасности в Интернете: вы просто принимаете любой сертификат без предварительной проверки. См. Ответ @Ludovic для функции, которая проверяет перед принятием. – ssamuel

3

Я тоже сталкиваюсь с ошибкой.

Я попробовал ServicePointManager.ServerCertificateValidationCallback и ServicePointManager.CertificatePolicy, но все еще не работает.

Я гнев. построить cURL wraper. Это отлично работает для моего игрушечного проекта.

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

У меня была та же проблема. Когда ответ HTTP выдает это исключение, то я:

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

это импортирует недостающие сертификаты и исключение никогда не случилось.

2

В первом ответе сказано: Моно ни на что другое, кроме Windows, не приходит ни с чем, поэтому изначально он не доверяет никакому сертификату. Так что делать?

Вот хорошая статья о различных способов решения этой проблемы с точки зрения разработчика: http://www.mono-project.com/archived/usingtrustedrootsrespectfully/

Краткое резюме: Вы можете:

  • игнорировать озабоченность по поводу безопасности
  • Ignore проблема
  • Сообщите пользователю и отменит
  • Позвольте пользователю знать и давать ему выбор для продолжения собственного риска

Приведенная выше ссылка содержит примеры кода для каждого случая.

+1

вызов mozroots --import --sync должен исправить его для вас, добавив сертификаты по умолчанию из mozilla в хранилище моно хранилищ. – ScottB

42

У меня была та же проблема с Unity (которая также использует моно), и this post помог мне решить эту проблему.

Просто добавьте следующую строку, прежде чем сделать запрос:

ServicePointManager.ServerCertificateValidationCallback = MyRemoteCertificateValidationCallback; 

И этот метод:

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

Это должно сработать на самом деле, но в моем «свариваемом» автогенераторе, похоже, нет. См. Https://github.com/swagger-api/swagger-editor/issues/1034 – loretoparisi

+2

Работает как шарм. (Y) –

+0

Существует потенциальная проблема, что «цепочка».Build (cert) 'всегда возвращает true в моно-запросе на [bad ssl] (https://badssl.com). – sakiM

2

Другим решением для единства является инициализация ServicePointManager один раз, чтобы всегда принимать сертификаты. Это работает, но, очевидно, небезопасно.

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

Вы можете упростить этот ответ в одной строке: 'ServicePointManager.ServerCertificateValidationCallback + = (p1, p2, p3, p4) => true;' – Andy

0

Вы можете установить Mono реализации TLS в ИО Строить и все будет работать нормально, как описано здесь: http://massivepixel.co/blog/post/xamarin-studio-6-certificate-unknown (хотя Mono TLS не поддерживает новые версии TLS, но я пока не наткнулся на вопрос, что это это проблема).