SSL(Secure Socket Layer,安全通訊端層)是一種保證網路上的兩個節點進行安全通訊的協定。IETF(Interet Engineering Task Force)國際組織對 SSL 作了標準化,制定了 RFC2246 規範,並將其稱為傳輸層安全(Transport Layer Security,TLS)
SSL 和 TLS 都建立在 TCP/IP 的基礎上,一些應用層協定,如 HTTP 和 IMAP,都可以採用 SSL 來保證安全通訊。建立在 SSL 協定上的 HTTP 被稱為 HTTPS 協定。HTTP 使用的預設埠為 80,而 HTTPS 使用的預設埠為 443
SSL 採用加密技術來實現安全通訊,保證通訊資料的保密性和完整性,並且保證通訊雙方可以驗證對方的身份
當客戶與伺服器進行通訊時,通訊資料有可能被網路上的其他計算機非法監聽,SSL 使用加密技術實現對談雙方資訊的安全傳遞。加密技術的基本原理是,資料從一端傳送到另一端時,傳送者先對資料加密,然後把它傳送給接收者。這樣,在網路上傳輸的是經過加密的資料。如果有人在網路上非法截獲了這批資料,由於沒有解密的金鑰,數無法獲取真正的原始資料。接收者接收到加密的資料後,先對資料解密,然後處理
除了對資料加密通訊,SSL 還採用了身份認證機制,確保通訊雙方都可以驗證對方的真實身份。這和生活中我們使用身份證來證明自己的身份很相似,比如你到銀行去取錢,你自稱自己是張三,如何讓對方相信你的身份呢?最有效的辦法就是出示身份證。每人都擁有唯一的身份證,這個身份證上記錄了你的真實資訊。身份證由國家權威機構頒發,不允許偽造。在身份證不能被別人假冒複製的前提下,只要你出示身份證,就可以證明你自己的身份
個人可以通過身份證來證明自己的身份,對於一個單位,比如商場,可以通過營業執照來證明身份。營業執照也由國家權威機構頒發,不允許偽造,它保證了營業執照的可信性
SSL 通過安全證書來證明客戶或伺服器的身份,當客戶通過安全的連線和伺服器通訊時,伺服器會先向客戶出示它的安全證書,這個證書宣告該伺服器是安全的,而且的確是這個伺服器,每一個證書在全世界範圍內都是唯一的,其他非法伺服器無法假冒,可以把安全證書比作電子身份證
對於單個客戶來說,到公認的權威機構去獲取安全證書是雲件麻煩的事。為了擴大客戶群並且便於客戶的存取,許多伺服器不要求客戶出示安全證書。在某些情況下,伺服器也會要求客戶出示安全證書,以便核實該客戶的身份,這主要是在 B2B 事務中
獲取安全證書有兩種方式,一種方式是從權威機構獲得證書,還有一種方式是建立自我簽名證書
安全證書由國際權威的證書機構頒發,它們保證了證書的可信性。申請安全證書時,必須支付一定的費用。一個安全證書只對一個 IP 地址有效,如果使用者的系統環境中有多個 IP 地址須為每個 IP 地址都購買安全證書
在某些場合,通訊雙方只關心資料在網路上可以被安全傳輸,並不需要對方進行身份驗證,在這種情況下,可以建立自我簽名的證書。就像使用者自己製作的名片,缺乏權威性,達不到身份認證的目的。當你向對方遞交名片時,名片上聲稱你是某個大公司的老總,信不信只能由對方自己去判斷
既然自我簽名證書不能有效地證明自己的身份,那麼有何意義呢?在技術上,無論是從權威機構獲得的證書,還是自己製作的證書,採用的加密技術都是一樣的,使用這些證書,都可以實現安全地加密通訊
安全證書既包含了用於加密資料的金鑰,又包含了用於證實身份的數位簽章。安全證書採用公鑰加密技術,公鑰加密指使用一對非對稱的金鑰進行加密或解密。每一對金鑰由公鑰和私鑰組成,公鑰被廣泛釋出,私鑰是隱藏的,不公開。用公鑰加密的資料只能夠私鑰解密,反過來,使用私鑰加密的資料只能被公鑰解密
客戶與伺服器通訊時,首先要進行 SSL 握手,SSL 握手主要完成以下任務:
SSL 握手過程採用非對稱加密方法傳遞資料,由此來建立一個安全的對談。SSL 握手完成後,通訊雙方將採用對稱加密方法傳遞實際的應用資料。所謂對稱加密,指通訊雙方使用同樣的金鑰來加密資料
SSL 握手的具體流程如下:
JDK 提供了製作證書的工具 keytool,它的位置為:<JDK根目錄>\bin\keytool.exe
keytool 工具提出了金鑰庫的概念,金鑰庫中可以包含多個條目,每個條目包括一個自我簽名的安全證書以及一對非對稱金鑰
通過 keytool 工具建立金鑰庫的命令為:
keytool -genkeypair -alias weiqin -keyalg RSA -keystore C:\chapter15\test.keystore
命令的執行過程首先會提示輸入金鑰庫的密碼,然後提示輸入個人資訊,如姓名、組織單位和所在城市等,只要輸入真實資訊即可
以下命令檢視 test.keystore 密庫的資訊,會列出所包含的條目的資訊
keytool -list -v -keystore C:\chapter15\test.keystore -storepass "123456」
以下命令把 test.keystore 金鑰庫中別名為 weiqin 的條目匯出到一個安全證書,檔名為 weiqin.crt。weiqin.crt 文中包含了自我簽名的安全證書,以及金鑰對中的公鑰,但不包含金鑰對中的私鑰
keytool -export -alias weigin -keystore C:\chapter15\test.keystore
-file C:\chapter15\weigin.crt -storepass 「123456」
以下命令刪除 test.keystore 金鑰庫中的別名為 weiqin 的條目
keytool -delete -alias weigin -keystore C:\chapter15\test.keystore -storepass 「123456」
以下命令把 weiqin.crt 安全證書匯入 testTrust.keystore 密庫中生成別名為 weiqin 的條目,這個條目中包含金鑰對中的公鑰,但不包含金鑰對中的私鑰
keytool -import -alias weiqin
-keystore C:\chapter15\testTrust.keystore
-file C:\chapter15\weiqin.crt
-storepass "123456"
JSSE 封裝了底層複雜的安全通訊細節,使得開發人員能方便地用它來開發安全的網路應用程式
在進行安全通訊時,要求使用者端與伺服器端都支援 SSL 或 TCL 協定。使用者端與伺服器端可能都需要設定用於證實自身身份的安全證書,還要設定信任對方的哪些安全證書。更常見的情況是,伺服器端只需要設定用於證實自身身份的安全證書,而使用者端只需要設定信任伺服器的哪些安全證書
KeyStore 類用於存放包含安全證書的金鑰庫,以下程式程式碼建立了一個 KeyStore 物件,它從 test.keystore 金鑰庫檔案中載入安全證書
String passphrase = "123456";
//JKS是JDK 支援的KeyStore的型別
KeyStore keyStore = KeyStore.getInstance("JKS");
char[] password = passphrase.toCharArray();
//password引數用於開啟金鑰庫
keyStore.load(new FileInputStream("test.keystore"), password);
KeyManager 介面的任務是選擇用於證實自身身份的安全證書,把它傳送給對方。KeyManagerFactory 負責建立 KeyManager 物件,例如
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
keyManagerFactory.init(keyStore, password);
KeyManager[] keyManagers = keyManagerFactory.getKeyManagers();
TrustManager 介面的任務是,決定是否信任對方的安全證書。TruesManagerFactory 負責建立 TrustManager 物件,例如
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509");
trustManagerFactory.init (keyStore);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
SSLContext 類負責設定與安全通訊有關的各種資訊,比如使用的協定(SSL 或者 TLS),自身的安全證書以及對方的安全證書。SSLContext 還負責構造 SSLServerSocketFactory、SSLSocketFactory 和 SSLEngine 物件
以下程式程式碼建立並初始化了一個 SSLContext 物件,然後由它建立了一個 SSLServerSocketFactory 物件
SSLContext sslCtx = SSLContext.getInstance("TLS");//採用TLS協定
sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
SSLServerSocketFactory ssf = sslCtx.getServerSocketFactory();
SSLContext 的 init 方法的定義如下
public final void init(KeyManager[] km, TrustManager[] tm, SecureRandom random) throws KeyManagementException
SSLServerSocketFactory 類負責建立 SSLServerSocket 物件
SSLServerSocket serverSocket = (SSLServerSocket) sslServerSocketFactory.createServerSocket(8000); // 監聽埠8000
SSLServerSocketFactory 物件有兩種建立方法:
SSLServerSocketFactory 類的靜態 getDefault 方法返回一個預設的 SSLServerSocketFactory 物件,它與一個預設的 SSLContext 物件關聯,getDefault 方法的實現按照如下方式初始化這個預設的 SSLContext 物件
sslContext.init(null,null,null);
SSLSocketFactory 類負責建立 SSLSocket 物件
SSLSocket socket = (SSLSocket) sslSocketFactory.createSocket("localhost",8000);
SSLSocketFactory 物件有兩種建立方法
SSLSocketFactory 類的靜態 getDefault 方法返回一個預設的 SSLSocketFactory 物件,它與一個預設的 SSLContext 物件關聯,getDefault 方法的實現按照如下方式初始化這個預設的 SSLContext 物件
sslContext.init(null,null,null);
SSLSocket 類是 Socket 類的子類,因此兩者的用法有許多相似之處。此外,SSLSocket 類還具有與安全通訊有關的方法
客戶與伺服器在握手階段需要協商實際使用的加密套件,以下兩種情況都會導致握手失敗:
SSLSocket 類的 getSupportedCipherSuites 方法返回一個字串陣列,它包含當 SSLSocket 物件所支援的加索套件組。SSLSocket 類的 setEnabledCipherSuites(String[] suites) 方法設定當前 SSLSocket 物件的可使用的加密套件組,可使用的加密件組應該是所支援的加密套件組的子集
以下程式碼僅僅啟用了具有高加密強度的加密套件,這可以提高該通訊端的安全性,禁止那些不支援強加密的通訊端連線當前通訊端
String[] strongSuites = {
"SSL_RSA_WITH_RC4_128_MD5",
"SSL_RSA_WITH_RC4_128_SHA",
"SSL_RSA_WITH_3DES_EDE_CBC_SHA"
};
sslSocket.setEnabledCipherSuites(strongSuites);
SSL 握手需要花很長的時間,當 SSL 握手完成,會發出一個 HandshakeCompletedEvent 事件,該事件由 HandshakeCompletedListener 負責監聽。SSLSocket 類的 addHandshakeCompletedListener 方法負責註冊 HandshakeCompletedListener 監聽器
下例為 SSLSocket 註冊了 HandshakeCompletedListener
socket.addHandshakeCompletedListener {
new HandshakeCompletedListener() {
public void handshakeCompleted(HandshakeCompletedEvent event) {
System.out.println("握手結束");
System.out.println("加密套件為:" + event.getCipherSuite());
System.out.println("對談為:" + event.getSession());
System.out.println("通訊對方為:" + event.getSession().getPeerHost());
}
});
}
一個客戶程式可能會向一個伺服器的同一個埠開啟多個安全通訊端。如果對於每一安全連線都進行 SSL 握手,就會大大降低通訊效率。為了提高安全通訊的效率,SSL 協定允許多個 SSLSocket 共用同一個 SSL 對談。在同一個對談中,只有第一個開啟的 SSLSocket 要進行 SSL 握手,負責生成金鑰以及交換金鑰,其餘的 SSLSocket 都共用金鑰資訊。在一段合理的時間範圍內,如果客戶程式向一個伺服器的同一個埠開啟多個安全通訊端,JSSE 就會自動重用對談
SSLSocket 的 getSession 方法返回 SSLSocket 所屬的對談。SSLSocket 的 setEnableSessionCreation(boolean flag) 方法決定 SSLSocket 是否允許建立新的對談。flag 的預設值為 true。如果 flag 引數為 true,那麼對於新建立的 SSLSocket,如果當前已經有可用的對談,就直接加入該對談,如果沒有可用的對談,就建立一個新的對談。如果 flag 為 false 引數,那麼對於新建立的 SSLSocket,如果當前已經有可用的對談,就直接加入該對談,如果沒有可用的對談,那麼該 SSLSocket 無法與對方進行安全通訊
SSLSocket的 startHandshake 方法顯式地執行一次 SSL 握手,該方法具有以下用途:
socket.getSession().invalidate();
socket.startHandshake();
多數情況下使用者端無須向伺服器證實自己的身份,因此當一個通訊端無須向對方證實自己身份時,就稱它處於客戶模式,否則稱它處於伺服器模式。通訊雙方只能有一方處於伺服器模式,另一方則處於客戶模式
SSLSocket 的 setUseClientMode(boolean mode) 方法被用來設定客戶模式或者伺服器模式。如果 mode 引數為 true,就表示處於客戶模式,即無須向對方證實自己的身份;如果 mode 為 false,就表示處於伺服器模式,即需要向對方證實自己的身價
當 SSL 初始握手已經開始,就不允許再呼叫 SSLSocket 的 seUseClientMode(boolean mode) 方法,否則會導致異常
當 SSLSocket 處於伺服器模式,還可以通過以下方法來決定是否要求對方提供身份認證:
SSLServerSocket 類是 ServerSocket 類的子類,因此兩者的用法有許多相似之處。此外,SSLServerSocket 類還具有與安全通訊有關的方法,這些方法與 SSLSocket 類中的同名方法具有相同的作用
SSLEngine 類與 SocketChannel 類聯合使用,就能實現非阻塞的安全通訊。SSLEngine 類封裝了與安全通訊有關的細節,把應用程式傳送的應用資料打包為網路資料,打包指對應用資料進行加密,加入 SSL 握手資料,把它變為網路資料。SSLEngine 類還能把接收的網路資料展開為應用資料,展開指對網路資料解密,並且去除其中的 SSL 握手資料,從而還原為應用程式可以處理的應用資料。SSLEngine 類的 wrap 方法負責打包應用資料,unwrap 方法負責展開網路資料
public class SSLEngineDemo {
private static boolean logging = true;
private SSLContext sslc;
private SSLEngine clientEngine; //使用者端Engine
private ByteBuffer clientOut; //存放使用者端傳送的應用資料
private ByteBuffer clientIn; //存放使用者端接收到的應用資料
private SSLEngine serverEngine; //伺服器端Engine
private ByteBuffer serverOut; //存放伺服器端傳送的應用資料
private ByteBuffer serverIn; //存放伺服器端接收到的應用資料
private ByteBuffer cTOs;//存放使用者端向伺服器端傳送的網路資料
private ByteBuffer sTOc;//存放伺服器向客戶海傳送的網路資料
//設定金鑰庫檔案和信任庫檔案以及口令
private static String keyStoreFile = "test.keystore";
private static String trustStorefile = "test.keystore";
private static String passphrase = "123456";
public static void main(String args[]) throws Exception {
SSLEngineDemo demo = new SSLEngineDemo();
demo.runDemo();
}
/**初始化SSLContext*/
public SSLEngineDemo() throws Exception {
KeyStore ks = KeyStore.getInstance("JKS");
KeyStore ts = KeyStore.getInstance("JKS");
char[] password = passphrase.toCharArray();
ks.load(new FileInputStream(keyStoreFile), password);
ts.load(new FileInputStream(trustStoreFile), password);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, password);
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(ts);
SSLContext sslCtx = SSLContext.getInstance("TLS");
sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
sslc = sslCtx;
}
private void runDemo() throws Exception {
boolean dataDone = false;
/*建立使用者端以及伺服器端的SSLEngine*/
serverEngine = sslc.createSSLEnqine();
serverEngine.setUseClientMode(false);
serverEngine.setNeedClientAuth(true);
clientEngine = sslc.createSSLEngine("client", 80);
clientEngine.setUseClientMode(true);
/*建立使用者端以及伺服器的應用衝區和網路緩衝區*/
SSLSession session = clientEngine.getsSession();
int appBufferMax = session.getApplicationBufferSize();
int netBufferMax = session.getPacketBufferSize();
clientIn = ByteBuffer.allocate(appBufferMax + 50);
serverIn = ByteBuffer.allocate(appBufferMax + 50)
cTOs = ByteBuffer.allocateDirect(netBufferMax);
sTOc = ByteBuffer.allocateDirect(netBufferMax);
clientOut = ByteBuffer.wrap("Hi Server, I'm Client".getBytes());
serverOut = ByteBuffer.wrap("Hello Client, I'm Server".getBytes());
SSLEngineResult clientResult;
SSLEngineResult serverResult;
while (!isEnqineClosed(clientEngine) || !isEngineClosed(serverEngine)) {
log("==================");
//使用者端打包應用資料
clientResult = clientEngine.wrap(clientOut, cTOs);
log("client wrap:", clientResult);
//完成握手任務
runDelegatedTasks(clientResult, clientEngine);
//伺服器端打包應用資料
serverResult = serverEngine.wrap(serverOut, sTOc);
log("server wrap:", serverResult);
//完成握手任務
runDelegatedTasks(serverResult, serverEngine);
cTOs.flip();
sTOc.flip();
log("---------------");
//使用者端展開網路資料
clientResult = clientEngine.unwrap(sTOc, clientIn);
log("client unwrap:", clientResult);
//完成握手任務
runDelegatedTasks(clientResult, clientEngine);
//伺服器端展開網路資料
serverResult = serverEngine.unwrap(cTOs, serverIn);
log("server unwrap:", serverResult);
//完成握手任務
runDeleqatedTasks(serverResult, serverEngine);
cTOs.compact();
sTOc.compact();
if (!dataDone && (clientOut.limit() == serverIn.position()) && (serverOut,limit()=m clientIn.position())) {
checkTransfer(serverOut, clientIn);
checkTransfer(clientOut, serverIn);
log("\tClosing clientEngine's *OUTBOUND*...");
clientEngine.closeOutbound();
dataDone = true;
}
}
}
/**當SSLEngine的輸出與輸入都關閉時,意味著SSLEngine被關閉*/
private static boolean isEngineClosed(SSLEngine engine) {
return(engine.isOutboundDone() && engine.isInboundDone());
}
/**執行SSL握手任務*/
private static void runDelegatedTasks(SSLEngineResult result, SSLEngine engine) throws Exception {
if(result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
Runnable runnable;
while((runnable = engine.getDelegatedTask()) != null) {
log("\trunning delegated task...");
runnable.run();
}
HandshakeStatus hsStatus = engine.getHandshakeStatus();
if(hsStatus == HandshakeStatus.NEED_TASK) {
throw new Exception("handshake shouldn't need additional tasks");
}
log("\tnew HandshakeStatus:" + hsStatus);
}
}
/**判斷兩個緩衝區內容是否相同*/
private static void checkTransfer(ByteBuffer a, ByteBuffer b) throws Exception {
a.flip();
b.flip();
if(!a.equals(b)) {
throw new Exception("pata didn't transfer cleanly");
} else {
log("\tData transferred cleanly");
}
a.position(a.limit());
b.position(b.limit());
a.limit(a.capacity());
b.limit(b.capacity());
}
private static boolean resultOnce = true;
/**輸出紀錄檔,列印SSLEngineResult的結果*/
private static void log(String str, SSLEngineResult result) {
if(resultOnce) {
resultOnce = false;
System.out.println("The format of the SSLEngineResult is: \n"
+"\t\"getStatus() / getHandshakeStatus()\」+\n"
+"\t\"bytesConsumed() / bytesProduced()\"\n");
}
HandshakeStatus hsStatus = result.getHandshakeStatus();
log(str + result,getStatus() + "/" + hsStatus + "," + result,bytesConsumed() + "/"
+ result.bytesProduced() + " bytes");
if (hsStatus == HandshakeStatus.FINISHED) {
log("\t...ready for application data");
}
}
/**輸出紀錄檔*/
private static void log(String str) {
System.out.printin(str);
}
}
public class EchoServer {
private int port = 8000;
private SSLServerSocket serverSocket;
public EchoServer() throws Exception {
//輸出跟蹤紀錄檔
SSLContext context = createSSLContext();
SSLServerSocketFactory factory = context.getServerSocketFactory();
serverSocket = (SSLServerSocket)factory.createServerSocket(poxt);
String[] supported = serverSocket.getSupportedCipherSuites();
serverSocket.setEnabledCipherSuites(supported);
}
public SSLContext createSSLContext() throws Exception {
//伺服器用於證實自己身份的安全證書所在的金鑰庫
String keyStoreFile = "test.keystore";
String passphrase = "123456";
KeyStore ks = KeyStore.getInstance("JKS");
char[] password = passphrase.toCharArray();
ks.load(new FileInputStream(keyStoreFile), password);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, password);
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(kmf.getKeyManagers(), null, null);
//當要求使用者端提供安全證書時,伺服器端可建立TrustManagerFactory,
//並由它建立TrustManager,TrustManger根據與之關聯的KeyStore中的資訊
//決定是否相信客戶提供的安全證書
//使用者端用於證實自己身份的安全證書所在的金鑰庫
//String trustStorefile = "test.keystore";
//KeyStore ts = KeyStore.getInstance("JKS");
//ts.load(new FileInputStream(trustStoreFile), password);
//TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
//tmf.init(ts);
//sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
return sslContext;
}
private PrintWriter getWriter(Socket socket) throws IOException {
OutputStream socketOut = socket.getOutputStream();
return new PrintWriter(socketOut, true);
}
private BufferedReader getReader(Socket socket) throws IOException {
InputStream socketIn = socket.getInputstream();
return new BufferedReader(new InputStreamReader(socketIn));
}
public void service() {
while (true) {
Socket socket = null;
try {
//等特客戶連線
socket = serverSocket.accept();
BufferedReader br = getReader(socket);
PrintWriter pw = getWriter(socket);
String msg = null;
while ((msg = br.readLine()) != null) {
System.out.println(msg);
if (msg.equals("bye")) {
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(socket != null) socket.close(); //斷開連線
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static void main(String args[]) throws Exception {
new EchoServer().service();
}
}
public class EchoClient {
private String host = "localhost";
private int port = 8000;
private SSLSocket socket;
public EchoClient() throws IOException {
SSLContext context = createSSLContext();
SSLSocketFactory factory = context.getSocketFactory();
socket = (SSLSocket)factory.createSocket(host,port);
Stringl] supported = socket.getSupportedCipherSuites();
socket.setEnabledCipherSuites(supported);
}
public SSLContext createSSLContext() throws Exception {
String passphrase = "123456";
char[] password = passphrase.toCharArray();
//設定使用者端所信任的安全證書所在的金鑰庫
String trustStoreFile = "test.keystore";
KeyStore ts = KeyStore.getInstance("JKS");
ts.load(new FileInputStream(trustStoreFile), password));
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(ts);
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, tmf.getTrustManagers(), null);
return sslContext;
}
public static void main(String args[]) throws IOException {
new EchoClient().talk();
}
public void talk()throws IOException {
try {
BufferedReader br = getReader(socket);
PrintWriter pw = getWriter(socket);
BufferedReader localReader = new BufferedReader(new InputStreamReader(System.in));
String msg = null;
while((msg = localReader.readLine()) != null) {
pw.println(msg);
System.out.println(br.readline());
if(msg.equals("bye")) {
break;
}
}
} catch(IOException e) {
e.printStackTrace();
} finally {
try {
if(socket != null) socket.close(); //斷開連線
} catch (IOException e) {
e.printStackTrace();
}
}
}
private PrintWriter getWriter(Socket socket) throws IOException {
OutputStream socketOut = socket.getoutputstream();
return new PrintWriter(socketOut, true);
}
private BufferedReader getReader(Socket socket) throws IOException {
InputStream socketIn = socket.getInputstream();
return new BufferedReader(new InputStreamReader(socketIn));
}
}