隨筆 - 5, 文章 - 0, 評論 - 1, 引用 - 0
          數據加載中……

          使用SSL構建安全的Socket(轉)

          bromon原創?版權所有

          ?SSL(安全套接層)是Netscape公司在1994年開發的,最初用于WEB瀏覽器,為瀏覽器與服務器間的數據傳遞提供安全保障,提供了加密、來源認證和數據完整性的功能?,F在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

          posted on 2007-03-15 21:15 黑馬_2046 閱讀(1049) 評論(0)  編輯  收藏 所屬分類: 常用代碼


          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 嵊泗县| 紫金县| 隆昌县| 达州市| 观塘区| 恭城| 丽水市| 炎陵县| 弥勒县| 离岛区| 佛山市| 邵东县| 夹江县| 湟中县| 贵州省| 三亚市| 维西| 玉溪市| 仁寿县| 平和县| 清河县| 金堂县| 珠海市| 汾西县| 丁青县| 海原县| 聂拉木县| 高阳县| 江门市| 沙雅县| 黄山市| 扎囊县| 合肥市| 保靖县| 会宁县| 东阳市| 浦城县| 柳江县| 云阳县| 灵武市| 合作市|