2013-04-17 81 views
2

我正在嘗試開發一個透明代理,將來自客戶端的不安全http流量作爲安全https流量轉發到服務器,然後再轉發回去。爲了更好地說明我的觀點,請看下面的圖片。透明代理 - 從端口80到443

Rich Picture

讓我們假設,由於各種原因,客戶端將只使用HTTP,他們不能使用端口443進行HTTPS。由於有些服務器並不從80端口接受流量,我的代理需要他們重新路由到端口443這是一個可能的方案:

  1. 從客戶端接收它們爲首的80端口向www.google.com數據與https://www.google.com
  2. 初始化連接到端口443(做握手等)從客戶
  3. 加密數據,並將它們從https://www.google.com發送到https://www.google.com使用端口443
  4. 接收響應,解密他們,送他們回客戶端端口80.

由於這是一個透明的代理,客戶端(在我的情況下很多)不應該需要任何額外的配置。網絡已配置好,以便他們的流量通過我的節點。目前,我的節點只是簡單地重新路由數據,並在它們包含病毒時阻止它們。這是通過使用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在雙方都是錯誤的。

什麼是最好的方式來開發這樣的代理?

編輯:有沒有辦法讓TcpListener或HttpListener可以充當透明代理? (沒有在客戶端計算機上配置)。 HttpListener確切知道客戶端正在嘗試連接?

+0

我編輯了自己的冠軍。請參閱:「[應該在其標題中包含」標籤「](http://meta.stackexchange.com/questions/19190/)」,其中的共識是「不,他們不應該」。 –

+0

我並不完全瞭解這個問題,但是如果您需要對現有數據進行SSL加密/解密,請查看我們的SecureBlackbox中的TElSSLClient和TElSSLServer類(http://www.eldos.com/sbb/net- ssl.php)。它們獨立於傳輸(您的代碼提供傳輸),也許您可​​以使用它們代替SSLStream。 –

+0

不幸的是,這將不起作用,因爲客戶端也將等待ACK數據包(不僅僅是HTTP響應) –

回答

0

我不明白爲什麼需要從SslStream讀取加密數據。如果我正確地讀你的描述,你應該只需要:

  • 等待客戶端連接
  • 當客戶端連接,連接到服務器
  • 裹在SslStream服務器連接的NetworkStreamAuthenticateAsClient
  • 一旦被認證,在平行:從所述客戶端
    • 讀取數據並將其寫入到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); 
} 
+0

我從客戶端接收TCP數據包(沒有HTTP負載,例如ACK)。 SSLStream本身處理TCP數據包,所以我無法轉發從客戶端接收到的tcp數據包,因爲由於無效的ACK,會不斷髮生重新傳輸。如果SSLStream本身沒有處理TCP數據包,那麼你的建議(它是currentyl的實現)將完美工作。不知道我是否足夠好解釋了這一點。如果您需要更多信息,請與我們聯繫。 –

+0

我沒有使用TcpClient從客戶端讀取數據。我使用原始套接字來讀取數據,這就是爲什麼我還要從客戶端讀取TCP數據包的原因。希望這是有道理的。 –

+0

您需要在您的問題中提供一些實際的代碼才能獲得正確的答案。 – Iridium

相關問題