【问题标题】:Mono HTTPListener throws exception when using self signed SSL certificateMono HTTPListener 在使用自签名 SSL 证书时抛出异常
【发布时间】:2016-12-14 14:08:29
【问题描述】:

我使用 Mono HTTPListener 类在 Linux 上编写了一个小型 Web 服务器。它适用于 http 请求。但是,如果我使用自签名 SSL 证书(使用 openssl 创建并使用 httpcfg 安装),一旦来自浏览器的请求进入,它将引发不可捕获的异常。

例外是:

Unhandled Exception:
System.IO.IOException: The authentication or decryption has failed. ---> Mono.Security.Protocol.Tls.TlsException: The client stopped the handshake.
at Mono.Security.Protocol.Tls.SslServerStream.EndNegotiateHandshake (IAsyncResult asyncResult) <0xb4b079c8 + 0x001cf> in <filename unknown>:0 
at Mono.Security.Protocol.Tls.SslStreamBase.AsyncHandshakeCallback (IAsyncResult asyncResult) <0xb4b07428 + 0x0005f> in <filename unknown>:0

完整代码如下:

using System;
using System.Net;
using System.IO;
using System.Text;
using System.Threading;

namespace SSLTest
{
    class MainClass
    {
        static void Main ()
        {
            try
            {
                HttpListener l = new HttpListener ();
                l.Prefixes.Add ("https://*:8443/");
                l.Start ();

                Console.WriteLine("Server is running.");
                while (l.IsListening)
                {
                    //create the worker thread
                    HttpListenerContext ctx = l.GetContext();   //.GetContext() blocks until something comes in
                    if(ctx != null)
                    {
                        if(ctx.Request.RemoteEndPoint != null)
                        {
                            Thread workerThread = new Thread(() => RunWorker(ctx));
                            workerThread.Start();
                        }
                    }
                }
                Console.WriteLine("Server is stopped.");
            }
            catch(Exception ex) 
            {
                Console.WriteLine ("Exception in Main: " + ex);
            }
        }


        static void RunWorker(HttpListenerContext ctx)
        {
            try
            {
                if(ctx.Request != null)
                {
                    if(ctx.Request.RemoteEndPoint != null)
                    {
                        Console.WriteLine ("Got request from " + ctx.Request.RemoteEndPoint.ToString());
                        string rstr = "Test Website!\n" + DateTime.Now.ToString();
                        byte[] buf = Encoding.UTF8.GetBytes(rstr);
                        if(buf!=null)
                        {
                            ctx.Response.ContentLength64 = buf.Length;
                            ctx.Response.OutputStream.Write(buf, 0, buf.Length);
                        }
                    }
                }
            }
            catch(Exception ex)
            {
                Console.WriteLine ("@Exception in RunWorker: " + ex.Message);
            }
        }

    }
}

这是我第一次使用浏览器时的情况。浏览器会显示类似“不安全的证书!您要继续(不推荐)吗?”。如果我单击“是”并重新启动崩溃的服务器应用程序,它将从那一刻开始工作。

我该如何解决这个问题?

此外,我无法使用 try 块捕获此异常。它总是会终止我的申请。我怎样才能防止这种情况发生?

【问题讨论】:

标签: c# linux ssl mono


【解决方案1】:

这是 tcp 网络连接代码。

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Net.Security;
using System.Security.Authentication;
using System.Text;
using System.Threading;
using System.Web;

using Example.Lib.Common;
using Example.Lib.Models;

namespace Example.Lib.Net
{
    internal enum RequestType
    {
        None = 0,
        GET = 1,
        POST = 2,
        OPTIONS = 3
    }

    internal class TcpWebConnection : IDisposable
    {
        #region private members

        private bool m_IsDisposed = false;
        private bool m_IsSSL = false;
        private bool m_HasHeaders = false;
        private bool m_FileCreated = false;
        private bool m_IsFileUpload = false;
        private RequestType m_RequestType = RequestType.None;
        private string m_ReadData = string.Empty;
        private string m_Request = string.Empty;
        private string m_RemoteIP = string.Empty;
        private string m_AbsoluteURI = string.Empty;
        private string m_ContentType = string.Empty;
        private string m_TempFilename = string.Empty;
        private byte[] m_EndBoundaryBytes = null;
        private byte[] m_StartBoundaryBytes = null;
        private int m_ContentLength = 0;
        private long m_StartBoundaryIndex = -1;
        private long m_EndBoundaryIndex = -1;
        private long m_BytesRead = 0;
        private NameValueCollection m_QueryString = null;
        private string[] m_Segments = new string[1];
        private string m_HttpVersion = "HTTP/1.1";
        private byte[] m_PostData = null;
        private byte[] m_Buffer = new byte[65535];
        private ReadWriteBuffer m_TempBuffer;
        private FileStream m_FileStream = null;
        private MemoryStream m_FullBuffer = new MemoryStream();
        private TcpClient m_Client = null;
        private System.IO.Stream m_NetworkStream = null;
        private TcpWebServer m_Parent = null;
        private Thread m_Thread_Read = null;
        private Timer m_Timer_Check = null;
        private DateTime m_LastRead = DateTime.Now;
        private AutoResetEvent m_WaitHandle_Write;

        #endregion private members

        #region constructors

        internal TcpWebConnection(TcpClient client, TcpWebServer parent, bool ssl)
        {
            m_WaitHandle_Write = new AutoResetEvent(false);
            m_TempBuffer = new ReadWriteBuffer(65535);
            m_IsSSL = ssl;
            m_Segments[0] = string.Empty;
            m_Client = client;
            m_Parent = parent;
            m_RemoteIP = ((IPEndPoint)m_Client.Client.RemoteEndPoint).Address.ToString();

            if (ssl)
            {
                m_NetworkStream = new SslStream(m_Client.GetStream(), false);
            }
            else
            {
                m_NetworkStream = m_Client.GetStream();
            }

            m_NetworkStream.ReadTimeout = 2000;

            m_Timer_Check = new Timer(Timer_Check_Callback, this, 2000, 2000);

            // start threads
            m_Thread_Read = new Thread(DoRead);
            m_Thread_Read.IsBackground = true;
            m_Thread_Read.Start();
        }

        #endregion constructors

        #region destructors

        ~TcpWebConnection()
        {
            try
            {
                if (m_Timer_Check != null) m_Timer_Check.Dispose();
                m_Timer_Check = null;
            }
            catch { } // if the timer was 
        }

        #endregion destructors

        #region internal properties

        internal bool IsLargeFileUpload { get; set; } = false;

        internal string TempFilename
        {
            get { return m_TempFilename; }
            set { m_TempFilename = value; }
        }

        /// <summary>
        /// Remote IP
        /// </summary>
        internal string RemoteIP
        {
            get { return m_RemoteIP; }
        }

        internal string AbsoluteURI
        {
            get { return m_AbsoluteURI; }
        }

        internal string ContentType
        {
            get { return m_ContentType; }
        }

        internal string[] Segments
        {
            get { return m_Segments; }
        }

        internal NameValueCollection QueryString
        {
            get { return m_QueryString; }
        }

        internal Stream NetworkStream
        {
            get { return m_NetworkStream; }
        }

        internal int ContentLength
        {
            get { return m_ContentLength; }
        }

        #endregion internal properties

        #region private methods

        private void Timer_Check_Callback(object state)
        {
            if ((DateTime.Now - m_LastRead).TotalSeconds > 15)
            {
                try
                {
                    Program.BlacklistIP(m_RemoteIP, "TcpWebConnection - Timer", "Connection Timed Out");
                    ProcessRequest(m_ReadData);
                    Dispose();
                }
                catch (Exception e) { }
            }
        }

        private void DoRead()
        {
            if (m_IsSSL)
            {
                try
                {
                    ((SslStream)m_NetworkStream).AuthenticateAsServer(m_Parent.ServerCertificate, false, SslProtocols.Tls, false);
                    ((SslStream)m_NetworkStream).BeginRead(m_Buffer, 0, m_Buffer.Length, new AsyncCallback(SslRead), m_NetworkStream);
                    m_NetworkStream.ReadTimeout = 5000;
                    m_NetworkStream.WriteTimeout = 5000;
                }
                catch (Exception e)
                {
                    //Console.WriteLine("SSL Auth Error: " + e.Message);
                }
            }
            else
            {
                NormalRead();
            }
        }

        private void UpdatePostData()
        {
            m_FullBuffer.Position = 0;
            byte[] fullBuffer = Common.Conversion.MemoryStreamToByteArray(m_FullBuffer);
            m_FullBuffer.Dispose();

            if (m_StartBoundaryIndex > -1 && m_EndBoundaryIndex > -1)
            {
                m_PostData = new byte[m_EndBoundaryIndex - m_StartBoundaryIndex];
                Array.Copy(fullBuffer, m_StartBoundaryIndex, m_PostData, 0, m_EndBoundaryIndex - m_StartBoundaryIndex);
            }
        }

        internal void SaveFile(string filepath)
        {
            try
            {
                UpdatePostData();
                if (m_PostData == null) return;

                if (!Directory.Exists(Path.GetDirectoryName(filepath)))
                {
                    Directory.CreateDirectory(Path.GetDirectoryName(filepath));
                }

                if (File.Exists(filepath))
                {
                    File.Delete(filepath);
                }

                using (FileStream output = new FileStream(filepath, FileMode.Create, FileAccess.Write))
                {
                    output.Write(m_PostData, 0, m_PostData.Length);
                }
            }
            catch (Exception e)
            {
                // report error
            }
        }

        private void AppendBuffer(byte[] newBuffer, int length)
        {
            // we need to keep a running buffer here, the last 1024 bytes?  how best to find the end boundary?  need to determine when the stream is finished!
            m_TempBuffer.Write(newBuffer, length);
            if (m_IsFileUpload)
            {
                if (m_EndBoundaryIndex < 0)
                {
                    m_EndBoundaryIndex = StreamHelper.LastIndexOf(m_TempBuffer.RawBytes, m_TempBuffer.Count, m_EndBoundaryBytes);

                    if (!IsLargeFileUpload && m_EndBoundaryIndex > -1)
                    {
                        m_EndBoundaryIndex = (m_FullBuffer.Length + length) - (m_TempBuffer.Count - m_EndBoundaryIndex);
                    }
                }
                if (m_StartBoundaryIndex < 0)
                {
                    m_StartBoundaryIndex = StreamHelper.IndexOf(m_FullBuffer, m_StartBoundaryBytes);

                    if (m_StartBoundaryIndex > -1)
                    {
                        m_StartBoundaryIndex = StreamHelper.IndexOf(m_FullBuffer, Encoding.UTF8.GetBytes("\r\n\r\n"), m_StartBoundaryIndex + m_StartBoundaryBytes.Length) + 4;
                    }
                }
            }

            if (m_StartBoundaryIndex == -1 || !IsLargeFileUpload) // if this is not a file upload because no start boundary has been found then write buffer to memory
            {
                m_FullBuffer.Write(newBuffer, 0, length);
            }
            else
            {
                if (!m_FileCreated) // we have never written to the file, dump the contents of the full buffer now
                {
                    bool exists = true;

                    while (exists)
                    {
                        m_TempFilename = Config.StaticConfig.TempFolder + "/" + Path.GetRandomFileName();
                        exists = File.Exists(m_TempFilename);
                    }

                    m_FileStream = new FileStream(m_TempFilename, FileMode.Create, FileAccess.Write);

                    m_FullBuffer.Position = m_StartBoundaryIndex;
                    m_FullBuffer.CopyTo(m_FileStream);
                    m_FileStream.Write(newBuffer, 0, length);

                    m_FileCreated = true;
                }
                else // we have previously written to the file, append new bytes
                {
                    if (m_EndBoundaryIndex == -1)
                    {
                        m_FileStream.Write(newBuffer, 0, length);
                    }
                    else
                    {
                        m_FileStream.Write(newBuffer, 0, length - m_EndBoundaryBytes.Length);
                    }
                }
            }
        }

        private void NormalRead()
        {
            try
            {
                int bufferSize = m_Buffer.Length;
                int bytesRead = m_Client.Client.Receive(m_Buffer, bufferSize, 0);

                while (bytesRead > 0 && !m_IsDisposed)
                {
                    m_LastRead = DateTime.Now;
                    m_BytesRead += bytesRead;

                    if (!m_HasHeaders || m_RequestType == RequestType.GET)
                    {
                        string sBuffer = Encoding.ASCII.GetString(m_Buffer, 0, bytesRead);
                        m_ReadData += sBuffer;
                    }

                    AppendBuffer(m_Buffer, bytesRead);
                    m_HasHeaders = UpdateUniqueHeaders();

                    if (!m_HasHeaders && m_BytesRead > 1024)
                    {
                        Program.BlacklistIP(m_RemoteIP, m_ReadData, "No HTTP headers found in the first 1024 bytes");
                        return;
                    }

                    if (m_RequestType != RequestType.POST)
                    {
                        break; // process the request
                    }
                    else if (m_EndBoundaryIndex != -1)
                    {
                        break; // process the request, we found our end boundary for posted data
                    }

                    bytesRead = m_Client.Client.Receive(m_Buffer, bufferSize, 0);
                }

                ProcessRequest(m_ReadData);
            }
            catch (Exception e)
            {
                // report error
            }
        }

        private void SslRead(IAsyncResult ar)
        {
            if (m_IsDisposed) return;

            try
            {
                int byteCount = -1;
                int bufferSize = m_Buffer.Length;

                m_LastRead = DateTime.Now;
                byteCount = m_NetworkStream.EndRead(ar);
                m_BytesRead += byteCount;

                if (!m_HasHeaders || m_RequestType == RequestType.GET)
                {
                    string sBuffer = Encoding.ASCII.GetString(m_Buffer, 0, byteCount);
                    m_ReadData += sBuffer;
                }

                AppendBuffer(m_Buffer, byteCount);
                m_HasHeaders = UpdateUniqueHeaders();

                if (!m_HasHeaders && m_BytesRead > 1024)
                {
                    Program.BlacklistIP(m_RemoteIP, m_ReadData, "No HTTP headers found in the first 1024 bytes");
                    return;
                }

                if (byteCount > 0)
                {
                    if (m_RequestType != RequestType.POST && m_RequestType != RequestType.None)
                    {
                        m_NetworkStream.BeginRead(m_Buffer, 0, bufferSize, new AsyncCallback(SslRead), m_NetworkStream);
                    }
                    else if (m_EndBoundaryIndex == -1) // as long as we haven't found the end of the stream continue reading
                    {
                        m_NetworkStream.BeginRead(m_Buffer, 0, bufferSize, new AsyncCallback(SslRead), m_NetworkStream);
                        return;
                    }
                }
            }
            catch (Exception e)
            {
                return;
            }

            ProcessRequest(m_ReadData);
        }

        private bool UpdateUniqueHeaders()
        {
            if (m_RequestType == RequestType.None && m_ReadData.Length > 8)
            {
                m_RequestType = (m_ReadData.StartsWith("GET ") ? RequestType.GET : m_RequestType);
                m_RequestType = (m_ReadData.StartsWith("POST ") ? RequestType.POST : m_RequestType);
                m_RequestType = (m_ReadData.StartsWith("OPTIONS ") ? RequestType.OPTIONS : m_RequestType);
            }

            if (m_RequestType == RequestType.GET || m_RequestType == RequestType.POST)
            {
                string request = m_ReadData;

                if (string.IsNullOrEmpty(m_HttpVersion)) m_HttpVersion = m_ReadData.Substring(request.IndexOf("HTTP", 1), 8);
                if (string.IsNullOrEmpty(m_ContentType)) m_ContentType = GetHeader(request, "Content-Type");
                if (m_ContentLength == 0)
                {
                    int cLength = 0;
                    int.TryParse(GetHeader(request, "Content-Length"), out cLength);
                    m_ContentLength = cLength;

                    if (m_ContentLength / 1024 / 1024 > 20)
                    {
                        IsLargeFileUpload = true; // data is sent directly to a file instead of saving in memory
                    }
                }
            }

            if (m_RequestType != RequestType.None && !string.IsNullOrEmpty(m_HttpVersion) && (!string.IsNullOrEmpty(m_ContentType) || m_RequestType != RequestType.POST))
            {
                if (m_RequestType == RequestType.POST)
                {
                    try
                    {
                        if (m_IsFileUpload == false)
                        {
                            m_IsFileUpload = Segments[1].Replace("/", "") == "upload";
                        }
                    }
                    catch { }

                    if (m_RequestType == RequestType.POST && m_StartBoundaryBytes == null)
                    {
                        m_StartBoundaryBytes = Encoding.ASCII.GetBytes(GetStartBoundary());
                        m_EndBoundaryBytes = Encoding.ASCII.GetBytes(GetEndBoundary());
                    }
                }

                if (string.IsNullOrEmpty(m_Request) && m_Segments.Length <= 1 && m_QueryString == null)
                {
                    // Extract the Requested Type and Requested file/directory
                    string m_Request = m_ReadData.Substring(0, m_ReadData.IndexOf("HTTP", 1) - 1);

                    //Replace backslash with Forward Slash, if Any
                    m_Request = m_Request.Replace("\\", "/");
                    m_Request = m_Request.Replace("GET ", "");
                    m_Request = m_Request.Replace("POST ", "");

                    Uri uri = new Uri("http://localhost" + m_Request);
                    NameValueCollection query = HttpUtility.ParseQueryString(uri.Query);
                    //SendHeader(sHttpVersion, "image/jpeg", Program.BlankImageBuffer.Length, " 200 OK");

                    m_AbsoluteURI = m_Request;
                    m_Segments = uri.Segments;
                    m_QueryString = query;
                }

                if (m_RequestType != RequestType.POST)
                {
                    return true;
                }
                else if (m_ContentLength > 0 && m_EndBoundaryBytes != null)
                {
                    return true;
                }
            }

            return false;
        }

        private string GetStartBoundary()
        {
            return "--" + m_ContentType.Split(';')[1].Split('=')[1];
        }

        private string GetEndBoundary()
        {
            return "--" + m_ContentType.Split(';')[1].Split('=')[1] + "--\r\n";
        }

        private string GetHeader(string request, string key)
        {
            string result = string.Empty;
            int iStartPos = request.IndexOf(key + ":", 0) + key.Length + 1;

            if (request.IndexOf(key + ":", 0) > -1)
            {
                // Get the HTTP text and version e.g. it will return "HTTP/1.1"
                int iEndPos = request.IndexOf("\r\n", iStartPos);
                result = request.Substring(iStartPos, iEndPos - iStartPos).Trim();
            }

            return result;
        }

        private void CleanFile()
        {
            try
            {
                if (!string.IsNullOrEmpty(m_TempFilename) && File.Exists(m_TempFilename))
                {
                    using (Stream stream = File.Open(m_TempFilename, FileMode.Open, FileAccess.Read))
                    {
                        byte[] buffer = new byte[1024];
                        stream.Read(buffer, 0, buffer.Length);
                        //stream.Position = 0;
                        //stream.Write(data, 0, data.Length);
                    }
                }
            }
            catch { }
        }

        private void ProcessRequest(string request)
        {
            try
            {
                if (request.Length < 5) return;

                List<string> headers = null;
                if (request.StartsWith("OPTIONS"))
                {
                    headers = GetCommonHeader("", 0);
                    headers.Add("Access-Control-Allow-Credentials: true");
                    headers.Add("Access-Control-Allow-Headers: Authorization, X-Mashape-Authorization, Accept, Content-Type, X-Requested-With, X-PINGOTHER, X-File-Name, Cache-Control");
                    headers.Add("Access-Control-Allow-Methods: PUT, POST, GET, OPTIONS");
                    headers.Add("Keep-Alive: timeout=15,max=100");
                    headers.Add("Access-Control-Allow-Origin: *");
                    headers.Add("Connection: close");

                    SendHeader(headers);
                    return;
                }

                UpdateUniqueHeaders();
                CleanFile();
                CloseFile();

                if (m_Timer_Check != null) m_Timer_Check.Dispose();
                string responseText = Program.ProcessRequest(this);
                if (string.IsNullOrEmpty(responseText)) responseText = "\r\n";

                byte[] buf = Encoding.ASCII.GetBytes(responseText);
                headers = GetCommonHeader("text/html", buf.Length, " 200 OK");
                headers.Add("Access-Control-Allow-Origin: *");
                SendHeaderAndData(headers, buf);
            }
            catch (Exception e) { }
            finally
            {
                Dispose();
            }
        }

        private void CloseFile()
        {
            try
            {
                if (m_FileStream != null)
                {
                    m_FileStream.Dispose();
                    m_FileStream = null;
                }
            }
            catch { }
        }

        /// <summary>
        /// This function send the Header Information to the client (Browser)
        /// </summary>
        /// <param name="sHttpVersion">HTTP Version</param>
        /// <param name="sMIMEHeader">Mime Type</param>
        /// <param name="iTotBytes">Total Bytes to be sent in the body</param>
        /// <param name="mySocket">Socket reference</param>
        /// <returns></returns>
        public List<string> GetCommonHeader(string mimeHeader = "text/html", int length = -1, string sStatusCode = " 200 OK", string filename = "", bool chunked = false)
        {
            // if Mime type is not provided set default to text/html

            List<string> headers = new List<string>();

            headers.Add(m_HttpVersion + sStatusCode);
            headers.Add("Server: ExampleTcpWebServer");

            if (!string.IsNullOrEmpty(mimeHeader))
            {
                headers.Add("Content-Type: " + mimeHeader);
            }

            if (length > -1)
            {
                headers.Add("Content-Length: " + length);
            }

            headers.Add("Date: " + DateTime.Now.ToUniversalTime().ToString("ddd, d MMM yyyy HH:mm:ss") + " GMT");

            if (!string.IsNullOrEmpty(filename))
            {
                headers.Add("Content-Disposition: attachment; filename=\"" + filename + "\"");
            }
            if (chunked)
            {
                headers.Add("Transfer-Encoding: chunked");
            }

            return headers;
        }

        public void SendHeader(List<string> headers)
        {
            string sHeader = string.Empty;

            foreach (string header in headers)
            {
                sHeader += header + "\r\n";
            }

            sHeader += "\r\n";

            byte[] bSendData = Encoding.ASCII.GetBytes(sHeader);
            SendToBrowser(bSendData, bSendData.Length);
        }

        public void SendHeaderAndData(List<string> headers, byte[] data)
        {
            string sHeader = string.Empty;

            foreach (string header in headers)
            {
                sHeader += header + "\r\n";
            }

            sHeader += "\r\n";

            byte[] bHeader = Encoding.ASCII.GetBytes(sHeader);
            byte[] combined = new byte[bHeader.Length + data.Length];

            Array.Copy(bHeader, combined, bHeader.Length);
            Array.Copy(data, 0, combined, bHeader.Length, data.Length);

            SendToBrowser(combined, combined.Length);
        }

        /// <summary>
        /// Sends data to the browser (client)
        /// </summary>
        /// <param name="bSendData">Byte Array</param>
        /// <param name="mySocket">Socket reference</param>
        public void SendToBrowser(byte[] bSendData, int length)
        {
            try
            {
                if (Common.TcpHelper.SocketConnected(m_Client.Client))
                {
                    if (m_IsSSL)
                    {
                        m_NetworkStream.Write(bSendData, 0, length);
                    }
                    else
                    {
                        m_Client.Client.Send(bSendData, length, 0);
                    }
                }
                else
                {
                    Dispose();
                }
            }
            catch (Exception e)
            {
                //Console.WriteLine("Error Occurred : {0} ", e);
            }
        }

        #endregion private methods

        #region IDisposable

        public void Dispose()
        {
            if (!m_IsDisposed)
            {
                m_IsDisposed = true;

                try
                {
                    if (!string.IsNullOrEmpty(m_TempFilename) && File.Exists(m_TempFilename))
                    {
                        File.Delete(m_TempFilename);
                    }
                }
                catch { }

                CloseFile();

                try
                {
                    m_Client.Client.Close(5);
                    m_Client.Close();
                    m_Client.Client.Dispose();
                }
                catch { }

                try
                {
                    m_NetworkStream.Dispose();
                }
                catch { }

                try
                {
                    if (Thread.CurrentThread != m_Thread_Read && m_Thread_Read.IsAlive)
                    {
                        m_Thread_Read.Join(1000);
                        if (m_Thread_Read.IsAlive) m_Thread_Read.Abort();
                    }
                }
                catch { }

                try
                {
                    m_ReadData = null;
                    m_PostData = null;
                    m_Buffer = null;
                    m_TempBuffer = null;

                    if (m_FullBuffer != null) m_FullBuffer.Dispose();
                    if (m_Timer_Check != null) m_Timer_Check.Dispose();
                    m_Timer_Check = null;
                }
                catch { }
            }
        }

        #endregion IDisposable
    }
}

【讨论】:

    【解决方案2】:

    应该通过未发布的错误修复 https://bugzilla.xamarin.com/show_bug.cgi?id=52675 修复...虽然我还没有机会测试。

    我也看到了这一点,目前正在尝试找到一种方法来处理异常。这看起来是 Mono 中的一个错误。

    当证书验证以任何方式失败时发生。

    只要证书公用名 (dns) 与我尝试向其发送获取请求的 URL 中使用的 dns 相同,我就获得了 CA 签名证书,该证书可以解决问题。如果我改为在 url 中指定公共 IP 地址(证书未注册),mono 应用程序将崩溃并出现未处理的异常。

    我们正在考虑的一个选项是实现一个基于 TCP 的网络服务器,该服务器将使用 TcpListener 而不是繁重的 HttpListener,这反过来会解决我们看到的其他问题,即单 httplistener 前缀在绑定到后面的内部 IP 时无法正常工作一个 NAT。这意味着证书也可以以编程方式绑定,只是需要更多的工作。

    下面是一个非常粗略的版本。这绝不是一个成品,但它可以帮助其他人走上同样的道路......我必须在两个答案中做到这一点,这是 tcp Web 服务器。

    using System;
    using System.Net;
    using System.Net.Sockets;
    using System.Security.Cryptography.X509Certificates;
    
    namespace Example.Lib.Net
    {
        internal class TcpWebServer : IDisposable
        {
            private TcpListener m_Listener = null;
            private bool m_IsSSL = false;
            private X509Certificate2 m_ServerCertificate = null;
    
            internal X509Certificate2 ServerCertificate
            {
                get { return m_ServerCertificate; }
                set { m_ServerCertificate = value; }
            }
    
            internal void Start(string ip, int port, bool useSsl = false)
            {
                if (useSsl) // for player streams always use ssl to
                {
                    m_IsSSL = true;
                    m_ServerCertificate = new X509Certificate2("./cert/cert.pfx", "pass");
    
                    X509Store store = new X509Store(StoreName.TrustedPublisher, StoreLocation.LocalMachine);
                    store.Open(OpenFlags.ReadWrite);
                    store.Add(m_ServerCertificate);
                    store.Close();
                }
    
                IPAddress ipAddr = IPAddress.Any;
                if (ip != "*") IPAddress.TryParse(ip, out ipAddr);
    
                try
                {
                    m_Listener = new TcpListener(ipAddr, port);
                    m_Listener.Start();
                    m_Listener.BeginAcceptTcpClient(OnClientAccepted, m_Listener);
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                }
            }
    
            private void OnClientAccepted(IAsyncResult ar)
            {
                TcpListener listener = ar.AsyncState as TcpListener;
    
                if (listener == null)
                    return;
    
                TcpClient client = listener.EndAcceptTcpClient(ar);
                client.ReceiveBufferSize = 65535;
                client.Client.ReceiveBufferSize = 65535;
    
                TcpWebConnection con = new TcpWebConnection(client, this, m_IsSSL);
    
                listener.BeginAcceptTcpClient(OnClientAccepted, listener);
            }
        }
    }
    

    【讨论】:

    • 我最终实现了基于 tcp 的 Web 服务器并以编程方式绑定了 ssl 证书。我在上面发布的错误将解决该问题,但是该错误是在 Mono 4.8 中发现的,并且从那以后没有在修订版中实现。不知道为什么没有实施它的动机。我建议采取创建自己的解析 http / https 的 TCP 侦听器的路线,这需要一些工作,但最终还是值得的。您还可以拉下单声道源,自己实施错误修复并构建自己的副本...
    • 您能分享一下您的实现方法吗?您是否设法实现了启用跨平台 SSL 的 Web 服务器?
    • 是的,它启用了 SSL 并且支持跨平台,但是它还没有完成,也没有严格遵循 http / https 官方规范。它可以满足我的需要,并且我会根据需要添加其他功能。可能还不够好,无法在此处作为整体解决方案发布。
    • 我收回它,我会用代码更新我的答案。
    • 太棒了!感谢分享!
    猜你喜欢
    • 2017-09-12
    • 1970-01-01
    • 2020-10-25
    • 2017-02-18
    • 2014-09-30
    • 2012-11-01
    • 1970-01-01
    • 2017-03-30
    相关资源
    最近更新 更多