SSL Tunnel implementation on Java(3)

最後的步驟其實簡單得很。我們客戶端有了 ServerSocket 和一個對外的SSL Socket 連線,伺服器端當然要一個 SSL 的 ServerSocket 和 普通對內 Socket 連線。SSL 的 Socket 我們可以以自行撰寫的 SocketFactory 產生(第一章的 MySocketFactory class),那麼 ServerSocket 呢?
程式碼:(摘自 JavaWorld)

private ServerSocket getServerSocket(String keyStore, String keyStorePWD, boolean requireClientAuthentication, int listenPort) throws IOException {
try {
Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
KeyStore keystore = KeyStore.getInstance("JKS");
keystore.load(new FileInputStream(keyStore), keyStorePWD.toCharArray());
// A KeyManagerFactory is used to create key managers
KeyManagerFactory kmf = KeyManagerFactory.getInstance("sunx509");
// Initialize the KeyManagerFactory to work with our keystore
kmf.init(keystore, keyStorePWD.toCharArray());
// An SSLContext is an environment for implementing JSSE
// It is used to create a ServerSocketFactory
SSLContext sslc = SSLContext.getInstance("SSLv3");
// Initialize the SSLContext to work with our key managers
sslc.init(kmf.getKeyManagers(), null, null);
// Create a ServerSocketFactory from the SSLContext
ServerSocketFactory ssf = sslc.getServerSocketFactory();
// Socket to me
SSLServerSocket serverSocket =
(SSLServerSocket) ssf.createServerSocket(listenPort);
serverSocket.setNeedClientAuth(requireClientAuthentication);
return serverSocket;
} catch (GeneralSecurityException e) {
e.printStackTrace();
}
return null;
}

這個方法的四個參數,keyStore 是到存儲證書檔案的路徑(證書檔案用 Portecle 產生,產生時請選 Keystore 作檔案格式),keyStorePWD 是設定 Keystore 時所輸入的密碼,requireClientAuthentication 是客戶端要不要提供證書認證(我選不了),listenPort 當然就是伺服器的端口。我們選 443 吧,因為肯定代理伺服器不會擋。

當我們開啟了隧道程式,通常也不會立即連線,而是到真正有程式希望透過隧道連線時才會建立隧道。

do {
Socket client = listener.accept();
Socket tunneler;
//Redirect both side traffic
if (this.isClientSide) {
tunneler = new SSLTunnelSocket(remoteAddr, remotePort);
} else {
tunneler = new Socket(remoteAddr, remotePort); connected.");
}
tunneler.setKeepAlive(true);
(new Redirector(client, tunneler)).start();
(new Redirector(tunneler, client)).start();
} while (true);

這是我為其中一個類別寫的程式碼。如果 isClient = true,建立普通ServerSocket,SSL Socket,不是的話,則建立 SSL ServerSocket,普通 Socket 連往本端的 localhost 程式。Redirector 是啥?那是負責交換兩個 Socket 通訊的 Thread:

class Redirector extends Thread {
Socket in, out;
public Redirector(Socket incomingSocket, Socket outgoingSocket) {
this.in = incomingSocket;
this.out = outgoingSocket;
}
public void run() {
StringBuffer interceptedContent = new StringBuffer();
try {
InputStream inInputStream = in.getInputStream();
OutputStream outOutputStream = out.getOutputStream();
int i;
BufferedInputStream bis = new BufferedInputStream(inInputStream, 1024);
while ((i = bis.read()) != -1) {
outOutputStream.write(i);
outOutputStream.flush();
interceptedContent.append(Character.toString((char)i));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

這段沒有特別做過 optimization,只是其中一方用了BufferedInputStream。不過速度其實不差,其實連線還是取決於代理伺服器的速度吧。

Leave a Reply

Your email address will not be published. Required fields are marked *