bromon原創?版權所有
?SSL(安全套接層)是Netscape公司在1994年開發的,最初用于WEB瀏覽器,為瀏覽器與服務器間的數據傳遞提供安全保障,提供了加密、來源認證和數據完整性的功能。現在SSL3.0得到了普遍的使用,它的改進版TLS(傳輸層安全)已經成為互聯網標準。SSL本身和TCP套接字連接是很相似的,在協議棧中,SSL可以被簡單的看作是安全的TCP連接,但是某些TCP連接的特性它是不支持的,比如帶外數據(out-of-bound)。
?在構建基于Socket的C/S程序時,通過添加對SSL的支持來保障數據安全和完整是不錯的方法。完善的Java為我們提供了簡單的實現方法:JSSE(Java安全套接字擴展)。JSSE是一個純Java實現的SSL和TLS協議框架,抽象了SSL和TLS復雜的算法,使安全問題變得簡單。JSSE已經成為J2SE1.4版本中的標準組件,支持SSL?3.0和TLS?1.0。我們將通過一個具體的例子演示JSSE的一些基本應用。例子中的服務器端將打開一個SSL?Socket,只有持有指定證書的客戶端可以與它連接,所有的數據傳遞都是加密的。
?構造一個SSLSocket是非常簡單的:
?SSLServerSocketFactory?factory=(SSLServerSocketFactory)SSLServerSocketFactory.getDefault();
?SSLServerSocket?server?=?(SSLServerSocket)?factory.createServerSocket(portNumber);
?SSLSocket?socket?=?(SSLSocket);
?但是執行這樣的程序會產生一個異常,報告找不到可信任的證書。SSLSocket和普通的Socket是不一樣的,它需要一個證書來進行安全認證。
?一、?證書
?生成一個CA證書,在命令行下執行:
?keytool?–genkey?–keystore?SSLKey?–keyalg?rsa?–alias?SSL
?黑體部分是用戶可以自己指定的參數,第一個參數是要生成的證書的名字,第二個參數是證書的別名。rsa指明了我們使用的加密方法。
?系統會要求輸入證書發放者的信息,逐項輸入即可,如下圖:
??
???系統生成的文件命將會和證書名相同。證書可以提交給權威CA認證組織審核,如果通過審核,組織會提供信任擔保,向客戶擔保你的連接是安全的。當然這不是必須的。在我們的例子中會把證書直接打包到客戶端程序中,保證客戶端是授權用戶,避免偽造客戶,所以不需要提交審核。
?二、?服務器端
?現在可以編寫服務器端的代碼,與普通的Socket代碼不同,我們需要在程序中導入證書,并使用該證書構造SSLSocket。需要的說明的是:
?●KeyStore?ks=KeyStore.getInstance("JKS");
?訪問Java密鑰庫,JKS是keytool創建的Java密鑰庫,保存密鑰。
?●?KeyManagerFactory?kmf=KeyManagerFactory.getInstance("SunX509");
?創建用于管理JKS密鑰庫的X.509密鑰管理器。
?●?SSLContext?sslContext=SSLContext.getInstance("SSLv3");
?構造SSL環境,指定SSL版本為3.0,也可以使用TLSv1,但是SSLv3更加常用。
?●sslContext.init(kmf.getKeyManagers(),null,null);
?初始化SSL環境。第二個參數是告訴JSSE使用的可信任證書的來源,設置為null是從javax.net.ssl.trustStore中獲得證書。第三個參數是JSSE生成的隨機數,這個參數將影響系統的安全性,設置為null是個好選擇,可以保證JSSE的安全性。
?完整代碼如下:
??1
/**//*
??2
?*SSL?Socket的服務器端
??3
?*@Author?Bromon
??4
?*/
??5
??6
?package?org.ec107.ssl;
??7
??8
?import?java.net.*;
??9
?import?javax.net.ssl.*;
?10
?import?java.io.*;
?11
?import?java.security.*;
?12
?13
?public?class?SSLServer
?14
?
{
?15
??static?int?port=8266;??//系統將要監聽的端口號,82.6.6是偶以前女朋友的生日^_^
?16
??static?SSLServerSocket?server;
?17
??
?18
??/**//*
?19
??*構造函數
?20
??*/
?21
??
?22
??public?SSLServer()
?23
??
{
?24
???
?25
??}
?26
??
?27
??
?28
??/**//*
?29
??*@param?port?監聽的端口號
?30
??*@return?返回一個SSLServerSocket對象
?31
??*/
?32
??
?33
??private?static?SSLServerSocket?getServerSocket(int?thePort)
?34
??
{
?35
???SSLServerSocket?s=null;
?36
???try
?37
???
{
?38
????String?key="SSLKey";??//要使用的證書名
?39
?40
????char?keyStorePass[]="12345678".toCharArray();??//證書密碼
?41
?42
????char?keyPassword[]="12345678".toCharArray();??//證書別稱所使用的主要密碼
?43
?44
????KeyStore?ks=KeyStore.getInstance("JKS");??//創建JKS密鑰庫
?45
?46
????ks.load(new?FileInputStream(key),keyStorePass);
?47
?48
????//創建管理JKS密鑰庫的X.509密鑰管理器
?49
????KeyManagerFactory?kmf=KeyManagerFactory.getInstance("SunX509");
?50
?51
????kmf.init(ks,keyPassword);
?52
?53
????SSLContext?sslContext=SSLContext.getInstance("SSLv3");
?54
?55
????sslContext.init(kmf.getKeyManagers(),null,null);
?56
??
?57
????//根據上面配置的SSL上下文來產生SSLServerSocketFactory,與通常的產生方法不同
?58
????SSLServerSocketFactory?factory=sslContext.getServerSocketFactory();
?59
?60
????s=(SSLServerSocket)factory.createServerSocket(thePort);
?61
?62
???}catch(Exception?e)
?63
???
{
?64
????System.out.println(e);
?65
???}
?66
???return(s);
?67
??}
?68
??
?69
??
?70
??public?static?void?main(String?args[])
?71
??
{
?72
???try
?73
???
{
?74
????server=getServerSocket(port);
?75
????System.out.println("在”+port+”端口等待連接
");
?76
?77
????while(true)
?78
????
{
?79
?????SSLSocket?socket=(SSLSocket)server.accept();
?80
?????
?81
?????//將得到的socket交給CreateThread對象處理,主線程繼續監聽
?82
?????new?CreateThread(socket);
?83
?????
?84
????}
?85
???}catch(Exception?e)
?86
???
{
?87
????System.out.println("main方法錯誤80:"+e);
?88
???}
?89
??}
?90
?}
?91
?92
?/**//*
?93
?*內部類,獲得主線程的socket連接,生成子線程來處理
?94
?*/
?95
?96
?class?CreateThread?extends?Thread
?97
?
{
?98
??static?BufferedReader?in;
?99
??static?PrintWriter?out;
100
??static?Socket?s;
101
??
102
??/**//*
103
??*構造函數,獲得socket連接,初始化in和out對象
104
??*/
105
??
106
??public?CreateThread(Socket?socket)
107
??
{
108
???try
109
???
{
110
????s=socket;
111
????in=new?BufferedReader(new?InputStreamReader(s.getInputStream(),"gb2312"));
112
113
????out=new?PrintWriter(s.getOutputStream(),true);
114
115
????start();??//開新線程執行run方法
116
117
???}catch(Exception?e)
118
???
{
119
????System.out.println(e);
120
???}
121
???
122
??}
123
??
124
??/**//*
125
??*線程方法,處理socket傳遞過來的數據
126
??*/
127
??
128
??public?void?run()
129
??
{
130
???try
131
???
{
132
????String?msg=in.readLine();
133
????System.out.println(msg);
134
????s.close();
135
???}catch(Exception?e)
136
???
{
137
????System.out.println(e);
138
???}
139
??}
140
?}
141
142
??將我們剛才生成的證書放到程序所在的目錄下,上面的代碼就可以在編譯之后執行:
?java?org.ec107.ssl.SSLServer
?在8266端口等待連接…
?三、?客戶端
?客戶端的代碼相對簡單,我們可以不在程序中指定SSL環境,而是在執行客戶端程序時指定。需要注意的是客戶端并沒有導入證書,而是采用了默認的工廠方法構造SSLSocket:
?●?SSLSocketFactory?factory=(SSLSocketFactory)SSLSocketFactory.getDefault();
?構造默認的工廠方法
?●Socket?s=factory.createSocket("localhost",port);
?打開一個SSLSocket連接
?

/**//*
?*SSL?Socket?的客戶端
?*@Author?Bromon
?*/

?package?org.ec107.ssl;

?import?java.net.*;
?import?javax.net.ssl.*;
?import?javax.net.*;
?import?java.io.*;

?public?class?SSLClient

?
{
??static?int?port=8266;
??public?static?void?main(String?args[])

??
{
???try

???
{
????SSLSocketFactory?factory=(SSLSocketFactory)SSLSocketFactory.getDefault();

????Socket?s=factory.createSocket("localhost",port);
????
????PrintWriter?out=new?PrintWriter(s.getOutputStream(),true);
????out.println("安全的說你好");
????out.close();
????s.close();
???}catch(Exception?e)

???
{
????System.out.println(e);
???}
??}
?}


?把服務器產生的證書(SSLKey)拷貝到程序所在的目錄,執行這個程序的時候需要向javax.net.ssl.trustStore環境變量傳入證書名:
?java?–Djavax.net.ssl.trustStore=SSLKey?org.ec107.ssl.SSLClient
?可以在服務器的控制臺看到客戶端發送過來的數據。
?執行客戶端可以有另一種方法,把證書拷貝到java?home/lib/security目錄下,名字改為jssecacerts,然后可以直接執行客戶端:
?java?org.ec107.ssl.SSLClient
?程序會自動的到上述目錄下去尋找jssecacerts文件作為默認的證書。需要注意的是這里的java?home并不是我們在安裝J2SE時指定的那個JAVA_HOME。可以執行一個程序來得到java?home的位置:
?public?class?GetJavaHome
?{
???public?static?void?main(String?args[])
???{
?????System.out.println(System.getProperty(“java.home”));
???}
?}
?一般情況下(windows?2K)hava?home的位置是在C:Program?FilesJavaj2re1.4.0_02,相對的,證書就應該拷貝到C:Program?FilesJavaj2re1.4.0_02libsecurity下,如果安裝了自帶JDK的Java?IDE,比如JBuilder,情況可能會有不同。
???如果程序客戶在不持有證書的情況下直接進行連接,服務器端會產生運行時異常,不允許進行連接。
?運行環境:windows?2K?server,j2sdk1.4.1