Android 用SSL構建安全的Socket


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的安全性。

 完整代碼如下:

  /*
    *SSL Socket的服務器端
    *@Author Bromon
    */
    
    package org.ec107.ssl;
    
    import java.net.*;
    import javax.net.ssl.*;
    import java.io.*;
    import java.security.*;
    
    public class SSLServer
    {
    static int port=8266;  //系統將要監聽的端口號,82.6.6是偶以前女朋友的生日^_^
    static SSLServerSocket server;
    
    /*
    *構造函數
    */
    
    public SSLServer()
    {
    
    }
    
    
    /*
    *@param port 監聽的端口號
    *@return 返回一個SSLServerSocket對象
    */
    
    private static SSLServerSocket getServerSocket(int thePort)
    {
    SSLServerSocket s=null;
    try
    {
    String key="SSLKey";  //要使用的證書名
    
    char keyStorePass[]="12345678".toCharArray();  //證書密碼
    
    char keyPassword[]="12345678".toCharArray();  //證書別稱所使用的主要密碼
    
    KeyStore ks=KeyStore.getInstance("JKS");  //創建JKS密鑰庫
    
    ks.load(new FileInputStream(key),keyStorePass);
    
    //創建管理JKS密鑰庫的X.509密鑰管理器
    KeyManagerFactory kmf=KeyManagerFactory.getInstance("SunX509");
    
    kmf.init(ks,keyPassword);
    
    SSLContext sslContext=SSLContext.getInstance("SSLv3");
    
    sslContext.init(kmf.getKeyManagers(),null,null);
    
    //根據上面配置的SSL上下文來產生SSLServerSocketFactory,與通常的產生方法不同
    SSLServerSocketFactory factory=sslContext.getServerSocketFactory();
    
    s=(SSLServerSocket)factory.createServerSocket(thePort);
    
    }catch(Exception e)
    {
    System.out.println(e);
    }
    return(s);
    }
    
    
    public static void main(String args[])
    {
    try
    {
    server=getServerSocket(port);
    System.out.println("在”+port+”端口等待連接...");
    
    while(true)
    {
    SSLSocket socket=(SSLSocket)server.accept();
    
    //將得到的socket交給CreateThread對象處理,主線程繼續監聽
    new CreateThread(socket);
    
    }
    }catch(Exception e)
    {
    System.out.println("main方法錯誤80:"+e);
    }
    }
    }
    
    /*
    *內部類,獲得主線程的socket連接,生成子線程來處理
    */
    
    class CreateThread extends Thread
    {
    static BufferedReader in;
    static PrintWriter out;
    static Socket s;
    
    /*
    *構造函數,獲得socket連接,初始化in和out對象
    */
    
    public CreateThread(Socket socket)
    {
    try
    {
    s=socket;
    in=new BufferedReader(new InputStreamReader(s.getInputStream(),"gb2312"));
    
    out=new PrintWriter(s.getOutputStream(),true);
    
    start();  //開新線程執行run方法
    
    }catch(Exception e)
    {
    System.out.println(e);
    }
    
    }
    
    /*
    *線程方法,處理socket傳遞過來的數據
    */
    
    public void run()
    {
    try
    {
    String msg=in.readLine();
    System.out.println(msg);
    s.close();
    }catch(Exception e)
    {
    System.out.println(e);
    }
    }
    }

 將我們剛才生成的證書放到程序所在的目錄下,上面的代碼就可以在編譯之后執行:
    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

 

SSL雙向認證java實現

 

http://www.blogjava.net/stone2083/archive/2008/07/10/169015.html

 

本文通過模擬場景,介紹SSL雙向認證的java實現

默認的情況下,我認為讀者已經對SSL原理有一定的了解,所以文章中對SSL的原理,不做詳細的介紹。
如果有這個需要,那么通過GOOGLE,可以搜索到很多這樣的文章。

模擬場景:
Server端和Client端通信,需要進行授權和身份的驗證,即Client只能接受Server的消息,Server只能接受Client的消息。

實現技術:
JSSE(Java Security Socket Extension)
是Sun為了解決在Internet上的安全通訊而推出的解決方案。它實現了SSL和TSL(傳輸層安全)協議。在JSSE中包含了數據加密,服務器驗證,消息完整性和客戶端驗證等技術。通過使用JSSE,開發人員可以在客戶機和服務器之間通過TCP/IP協議安全地傳輸數據

為了實現消息認證。
Server需要:
1)KeyStore: 其中保存服務端的私鑰
2)Trust KeyStore:其中保存客戶端的授權證書
同樣,Client需要:
1)KeyStore:其中保存客戶端的私鑰
2)Trust KeyStore:其中保存服務端的授權證書

我們可以使用Java自帶的keytool命令,去生成這樣信息文件
1)生成服務端私鑰,並且導入到服務端KeyStore文件中
keytool -genkey -alias serverkey -keystore kserver.keystore
過程中,分別需要填寫,根據需求自己設置就行
keystore密碼:123456 
名字和姓氏:stone
組織單位名稱:eulic
組織名稱:eulic
城市或區域名稱:HZ
州或省份名稱:ZJ
國家代碼:CN
serverkey私鑰的密碼,不填寫和keystore的密碼一致:123456
就可以生成kserver.keystore文件 
server.keystore是給服務端用的,其中保存着自己的私鑰

2)根據私鑰,導出服務端證書
keytool -export -alias serverkey -keystore kserver.keystore -file server.crt
server.crt就是服務端的證書

3)將服務端證書,導入到客戶端的Trust KeyStore中
keytool -import -alias serverkey -file server.crt -keystore tclient.keystore
tclient.keystore是給客戶端用的,其中保存着受信任的證書

采用同樣的方法,生成客戶端的私鑰,客戶端的證書,並且導入到服務端的Trust KeyStore中
1)keytool -genkey -alias clientkey -keystore kclient.keystore
2)keytool -export -alias clientkey -keystore kclient.keystore -file client.crt
3)keytool -import -alias clientkey -file client.crt -keystore tserver.keystore

如此一來,生成的文件分成兩組
服務端保存:kserver.keystore tserver.keystore
客戶端保存:kclient.keystore  tclient.kyestore

接下來,就采用JSSE,分別生成SSLServerSocket,SSLSocket

服務端,生成SSLServerSocket代碼

SSLContext ctx = SSLContext.getInstance("SSL");

KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");

KeyStore ks = KeyStore.getInstance("JKS");
KeyStore tks = KeyStore.getInstance("JKS");

ks.load(new FileInputStream("data/kserver.keystore"), SERVER_KEY_STORE_PASSWORD.toCharArray());
tks.load(new FileInputStream("data/tserver.keystore"), SERVER_TRUST_KEY_STORE_PASSWORD.toCharArray());

kmf.init(ks, SERVER_KEY_STORE_PASSWORD.toCharArray());
tmf.init(tks);

ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

return (SSLServerSocket) ctx.getServerSocketFactory().createServerSocket(DEFAULT_PORT);


客戶端,生成SSLSocket的代碼,大同小異

SSLContext ctx = SSLContext.getInstance("SSL");

KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");

KeyStore ks = KeyStore.getInstance("JKS");
KeyStore tks = KeyStore.getInstance("JKS");

ks.load(new FileInputStream("data/kclient.keystore"), CLIENT_KEY_STORE_PASSWORD.toCharArray());
tks.load(new FileInputStream("data/tclient.keystore"), CLIENT_TRUST_KEY_STORE_PASSWORD.toCharArray());

kmf.init(ks, CLIENT_KEY_STORE_PASSWORD.toCharArray());
tmf.init(tks);

ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

return (SSLSocket) ctx.getSocketFactory().createSocket(DEFAULT_HOST, DEFAULT_PORT);


如此,就完成了服務端和客戶端之間的基於身份認證的交互。

client采用kclient.keystore中的clientkey私鑰進行數據加密,發送給server
server采用tserver.keystore中的client.crt證書(包含了clientkey的公鑰)對數據解密,如果解密成功,證明消息來自client,進行邏輯處理

server采用kserver.keystore中的serverkey私鑰進行數據叫米,發送給client
client采用tclient.keystore中的server.crt證書(包含了serverkey的公鑰)對數據解密,如果解密成功,證明消息來自server,進行邏輯處理

如果過程中,解密失敗,那么證明消息來源錯誤。不進行邏輯處理。這樣就完成了雙向的身份認證。

下面我附上簡單的SSLServer.java SSLClient.java,供大家演示用。
啟動服務端的時候,大家不妨采用telnet 127.0.0.1 7777連接,看看能不能實現消息傳遞。

ssl demo
備注: 
demo是采用maven構建項目的 
demo文件的編碼是用utf8,為了避免中文亂碼,請把workspace設置成utf8編碼


所謂認證,是要對某台(當然可以是集群)服務器身份做認證.
認證方式有兩種:
自簽名認證:服務端生成key,然后根據key導出證書.公布於站點.
通過第三方認證機構認證:有服務端生成key,然后導出認證信息,交由天威誠信等第三方認證機構認證,最后生成證書,公布於站點.

客戶端,將證書下載,確認為可信任公司認證信息,並且導入到受信任區(trustscore),建立連接與服務端進行正常交互.至此,就完成了對服務端的認證.
所以客戶端必須將證書導入信任區.
java中,可以查看證書:
寫個簡單的方法:
FileInputStream fis = new FileInputStream("cert.cer");
CertificateFactory cf=CertificateFactory.getInstance("X509");
X509Certificate c=(X509Certificate) cf.generateCertificate(fis);
System.out.println(c.getSubjectDN());
//可以查看下X509Certificate的一些get方法.
其實keytool僅僅是jdk中的工具.
openssl是更常用的一個工具
可見:http://www.openssl.org/

用SSL構建安全的Socket
    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM