透明代理 - 从端口80到443

时间:2013-04-17 20:48:15

标签: c# http https proxy transparent

我正在尝试开发一个透明代理,它将来自客户端的不安全的http流量作为安全的https流量转发到服务器,然后再返回。为了更好地说明我的观点,请看下面的图像。

Rich Picture

假设由于各种原因客户端将仅使用HTTP,他们不能将端口443用于HTTPS。由于某些服务器不接受来自端口80的流量,因此我的代理需要将它们重新路由到端口443.这是一种可能的情况:

  1. 从客户端接收前往www.google.com
  2. 的端口80的数据
  3. 初始化与https://www.google.com的连接到端口443(握手等)
  4. 加密来自客户端的数据并将其发送到https://www.google.com到端口443。
  5. https://www.google.com接收回复,解密并将其发送回客户端到端口80.
  6. 由于这是一个透明的代理,客户端(在我的情况下很多)不需要任何额外的配置。已配置网络,以便其流量通过我的节点。目前,我的节点只是重新路由数据,并阻止一些包含病毒的数据。这是使用WinPcap来完成对低网络层的访问,但如果使用原始数据包(主要是关于握手)太难以完成,我愿意改变我的方法。

    我尝试了什么: 注意:www.google.com可以是网络上的任何网站。它只是用作一个例子。

    1. 铱星的建议。这不起作用,因为如果另一个应用程序使用TcpClient连接到它,TcpListener只接受新连接。由于这是一个透明的代理,因此不起作用。
    2. 改用HttpListener。但是,似乎这不起作用,因为它只接受与我自己的IP(而不是www.google.com)的连接。
    3. 像以前一样使用HttpListener,但这次我将数据包转发到我自己的IP,以便HttpListener接受连接。由于某种原因,这似乎不起作用(通过wireshark和TCP SYN数据包检查继续重新发送,不知道为什么或如何解决它。)
    4. 使用SslStream连接到https://www.google.com,然后从客户端接收的原始数据包中获取内容并将其写入流。这不起作用,因为SslStream自己处理TCP数据包(例如ACK或SYN)。该流只需要Http请求。它不起作用的另一个原因是因为我无法从流中读取TCP数据包的内容,只读取HTTP响应的内容(所以客户端坐在那里等待ACK)。
    5. 从客户端转发TCP数据包,因为它们是端口443,从服务器转发到端口80(因为只有HTTP请求和响应使用SSL加密,所以没有区别)并使用HttpRequest类来完成所有操作http请求和响应(因为该类自己处理握手)。这不起作用,因为双方的ACK都是错误的。
    6. 开发此类代理的最佳方式是什么?

      编辑:TcpListener或HttpListener是否可以充当透明代理? (无需在客户端计算机上进行配置)。什么时候HttpListener确实认识到客户端正在尝试连接?

1 个答案:

答案 0 :(得分:0)

我真的不明白为什么你需要从SslStream读取加密数据。如果我正确阅读您的描述,您只需要:

  • 等待客户端连接
  • 当客户端连接时,连接到服务器
  • NetworkStreamSslStream
  • 中包裹服务器连接的AuthenticateAsClient
  • 经过身份验证后,并行:
    • 从客户端读取数据并将其写入SslStream
    • SslStream读取数据并将其写入客户端

我不会在此过程中看到您需要查看来自SslStream的加密数据。

以下是一个非常基本的样本(虽然完全未经测试):

static void Main(string[] args)
{
    var listener = new TcpListener(IPAddress.Any, 11180);
    var clientConnection = listener.AcceptTcpClient();
    // When we get here, the client has connected, initiate the server connection
    var serverConnection = new TcpClient("your.server.name", 443);
    var serverStream = serverConnection.GetStream();
    var secureStream = new SslStream(serverStream);
    secureStream.AuthenticateAsClient("your.server.name");
    ConnectStreams(clientConnection.GetStream(), secureStream);
}

private static void ConnectStreams(Stream streamA, Stream streamB)
{
    ForwardStream(streamA, streamB, new byte[1024]);
    ForwardStream(streamB, streamA, new byte[1024]);
}

private static void ForwardStream(Stream source, Stream destination, byte[] buffer)
{
    source.BeginRead(buffer, 0, buffer.Length, r => Forward(source, destination, r, buffer), null);
}

private static void Forward(Stream source, Stream destination, IAsyncResult asyncResult, byte[] buffer)
{
    var bytesRead = source.EndRead(asyncResult);
    if (bytesRead == 0)
    {
        destination.Close();
        return;
    }
    destination.Write(buffer, 0, bytesRead);
    ForwardStream(source, destination, buffer);
}