91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java安全套接字編程以及keytool使用最佳實踐分析

發布時間:2021-10-29 17:47:37 來源:億速云 閱讀:151 作者:柒染 欄目:編程語言

這篇文章將為大家詳細講解有關Java安全套接字編程以及keytool使用最佳實踐分析,文章內容質量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。

概述

利用 Java 的 JSSE(Java Secure Socket Extension)技術,我們可以方便的編寫安全套接字程序,關于 JSSE 的介紹,可以參閱 Oracle         網站提供的 JSSE         指導。程序編寫過程中,我們需要將數字證書應用到代碼中。通常在正式的產品開發中,我們可以支付一定的費用,向正規認證機構,例如:Verisign、Geotrust、Thawte         等申請。

如果只是為了實驗,我們還可以使用 Java 自帶的 keytool 工具來制作證書。keytool 是密鑰和證書管理工具,生成的密鑰或證書,存放在 jks(Java Key         Store) 格式的文件里。從用途上來說,jks 格式的文件常用于:

1) 存儲非對稱密鑰對以及數字證書的證書庫;

2) 存儲信任證書列表的信任庫。

注意:不同版本的 Java 自帶的 keytool 命令行參數可能會略有不同。相比于 Java6,在 Java7 中 keytool         工具有如下改動:

-export 選項改名為 -exportcert

-genkey 選項改名為 -genkeypair

-import 選項改名為 – importcert

-keyclone 選項被廢棄

-identitydb 選項被廢棄

-selfcert 選項被廢棄

下面將以 Java7 中的 keytool 為例,對常見的用法進行說明。

使用 keytool 制作證書庫以及信任庫

生成非對稱密鑰以及自簽發證書

命令:keytool -genkeypair -alias TEST_ROOT -keystore test_root.jks

解釋:生成一對密鑰以及一個自簽發證書,其中私鑰和證書以別名 TEST_ROOT 存儲在 test_root.jks 文件中。

注意:使用上述命令時,命令行會交互的需要手動填寫密碼、CN、OU 等信息。也可以直接在命令行指定這些參數,詳情見 參考資料中列出的 keytool 使用幫助。

生成證書請求文件

命令:keytool -certreq -file test_server.csr -alias TEST_SERVER -keystore         test_server.jks

解釋:將別名為 TEST_SERVER 的公鑰和一些個人信息從 test_server.jks 文件中導出,作為證書請求文件。

簽發證書

命令:keytool -gencert -infile test_server.csr -outfile test_server.cer         -alias TEST_ROOT -keystore TEST_ROOT.jks

解釋:使用別名為 TEST_ROOT 的私鑰為 test_server.csr 簽發證書,并保存到 test_server.cer 文件中。

從 jks 文件中導出證書

命令:keytool -exportcert -alias TEST_ROOT -file test_root.cer -keystore         test_root.jks

解釋:從 test_root.jks 文件中導出別名為 TEST_ROOT 的證書并存放在 test_root.cer 文件中。

導入信任證書到 jks 文件

命令:keytool -importcert -alias TEST_ROOT -file test_root.cer -keystore         TEST_SERVER.jks

解釋:將證書 test_root.cer 以別名 TEST_ROOT 導入 TEST_SERVER.jks 中。

注意這里的目標 jks 文件里不含有指定的別名,此時的導入條目才會以 trustedCertEntry         信任證書的形式保存。

導入簽發證書到 jks 文件 ( 更新證書 )

命令keytool -importcert -alias TEST_SERVER -file         test_server.cer -keystore TEST_SERVER.jks

解釋將證書 test_server.cer 更新到已存在別名 TEST_SERVER 的         TEST_SERVER.jks 文件中

注意這里的命令和上述導入信任證書的命令在形式上完全一樣,但作用不同。

1. 這里的目標 jks 文件里要含有指定的別名,這樣 keytool 工具才會理解命令為更新證書,并以 PrivateKeyEntry 的形式保存。

2. 在更新被簽發證書之前,一定要先將相應的 CA 證書,導入進 jks 文件,否則會報錯“keytool 錯誤 : java.lang.Exception:         無法從回復中建立鏈”。

打印證書內容

命令keytool – printcert – v – file test_server.cer

解釋:將證書 test_server.cer 的內容打印出來

注意:也可以使用 -sslserver ip:port 的參數,直接從網絡上打印出某個 ssl server 提供的證書的內容,詳情見 參考資料中列出的 keytool 使用幫助。

顯示 jks 文件里的內容

命令:keytool – list – v – keystore test_server.jks

解釋:顯示 test_server.jks 里存儲的所有條目

注意:這里會要求提供 jks 文件的密碼,如果不輸入,也可以顯示出所有條目信息,但會提示“存儲在密鑰庫中的信息的完整性尚未得到驗證!”

從 jks 文件刪除條目

命令:keytool -delete -alias TEST_ROOT -keystore test_server.jks

解釋:從 test_server.jks 中刪除別名為 TEST_ROOT 的條目

安全套接字程序編寫的方法

使用 Java 編寫安全套接字程序,可以遵循一定的方法,如圖 1 所示,展示了相關的各個類之間的關系。其中         Keystore、KeyManagerFactory、TrustManagerFactory、SSLContext 可以稱之為“引擎類”(engine         class),對它們指定特定的參數 ( 例如:協議、算法等 ),就可以產生符合我們要求的,用于編程的對象實例。

圖 1. 相關類之間的關系

Java安全套接字編程以及keytool使用最佳實踐分析

(注:圖片引自 《 Java™ Secure Socket Extension (JSSE) Reference Guide 》)

編程的步驟可以簡單的小結為以下幾步:

1. 使用 Keystore 類將證書庫或信任庫文件加載進來;

2. 使用 KeyManagerFactory 和加載了證書庫的 Keystore 實例,產生 KeyManager 實例數組;

3. 使用 TrustManagerFactory 和加載了信任庫的 Keystore 實例,產生 TrustManager 實例數組;

4. 使用 SSLContext 初始化 KeyManager 實例數組和 TrustManager 實例數組,從而設定好通信的環境。

5. 利用 SSLContext 產生的 SSLSocket 或 SSLServerSocket 進行通信。

在編寫具體程序之前,我們需要利用前文對keytool 工具的知識介紹,準備如下 jks 文件:

1. test_root.jks:該文件中存有自簽發的證書,用作 CA 來簽發證書;

2. test_server_cert.jks:該文件中存有 CA 簽名的證書,用于 SSL/TSL 通信的服務端;

3. test_server_trust.jks:該文件中存有信任客戶端的證書,用于 SSL/TSL 通信的服務端;

4. test_client_cert.jks:該文件中存有 CA 簽名的證書,用于 SSL/TSL 通信的客戶端;

5. test_client_trust.jks:該文件中存有信任服務端的證書,用于 SSL/TSL 通信的客戶端。

假定每個 jks 文件的密碼都設定為“Testpassw0rd”,都存放在“D:”盤下。

通過系統屬性指定證書庫和信任庫

這種編寫方式比較簡單直觀,可以通過給 JVM 傳遞參數,或者在代碼中使用 System.setProperty() 方法,來指定通信需要的 jks 文件。

服務端程序

要運行如清單 1 所示的程序,可以在命令行添加如下虛擬機參數,指定服務端程序要使用的證書庫和密碼:

-Djavax.net.ssl.keyStore="D:/test_server_cert.jks"

-Djavax.net.ssl.keyStorePassword="Testpassw0rd"

注意到程序中 setNeedClientAuth(false),表示不需要驗證客戶端身份。如果這里設置為 true,則我們這里還需要指定信任庫和密碼:

-Djavax.net.ssl.trustStore="D:/test_server_trust.jks"

-Djavax.net.ssl.trustStorePassword="Testpassw0rd"

清單 1. 簡單的 SSL 通信服務端程序

import java.io.BufferedReader;   import java.io.IOException;   import java.io.InputStreamReader;   import java.net.Socket;    import javax.net.ssl.SSLServerSocket;   import javax.net.ssl.SSLServerSocketFactory;   import javax.net.ssl.SSLSocket;     public class Simple_SSLServerSocket{    // 定義了監聽端口號   private final static int LISTEN_PORT=54321;     public static void main(String args[]) throws IOException{      SSLServerSocket serverSocket=null;      SSLSocket clientSocket=null;      // 使用默認方式獲取套接字工廠實例     SSLServerSocketFactory ssf=(SSLServerSocketFactory)SSLServerSocketFactory.getDefault();     try{        serverSocket=(SSLServerSocket)ssf.createServerSocket(LISTEN_PORT);        // 設置不需要驗證客戶端身份       serverSocket.setNeedClientAuth(false);       System.out.println("SSLServer is listening on "+LISTEN_PORT+" port");        // 循環監聽端口,如果有客戶端連入就新開一個線程與之通信       while(true){          // 接受新的客戶端連接         clientSocket=(SSLSocket)serverSocket.accept();          ClientConnection clientConnection=new ClientConnection(clientSocket);          // 啟動一個新的線程         Thread clientThread=new Thread(clientConnection);          System.out.println("Client "+clientThread.getId()+" is connected");          clientThread.run();        }      }catch(IOException ioExp){        ioExp.printStackTrace();      }catch(Exception e){        e.printStackTrace();      }finally{        serverSocket.close();      }    }   }    class ClientConnection implements Runnable{    private Socket clientSocket=null;    public ClientConnection(SSLSocket sslsocket){      clientSocket=sslsocket;    }    public void run(){      BufferedReader reader=null;      // 將接收到的來自客戶端的文字打印出來     try{        reader=new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));        while(true){          String line=reader.readLine();          if(line==null){            System.out.println("Communication end.");            break;          }          System.out.println("Receive message: "+line);        }        reader.close();        clientSocket.close();      }catch(IOException ioExp){        ioExp.printStackTrace();      }catch(Exception e){        e.printStackTrace();      }    }   }

客戶端程序

對應于清單 1 所示的服務端程序,清單 2 是客戶端程序,需要在命令行添加如下虛擬機參數,指定信任庫和密碼:

-Djavax.net.ssl.trustStore="D:/test_client_trust.jks"

-Djavax.net.ssl.trustStorePassword="Testpassw0rd"

如果服務端程序 setNeedClientAuth(true) 要求驗證客戶端身份,則我們還需要指定證書庫和密碼:

-Djavax.net.ssl.keyStore="D:/test_client_cert.jks"

-Djavax.net.ssl.keyStorePassword="Testpassw0rd"

清單 2. 簡單的 SSL 通信客戶端程序

import java.io.BufferedReader;   import java.io.IOException;   import java.io.InputStreamReader;   import java.io.OutputStreamWriter;   import java.io.Writer;   import javax.net.ssl.SSLSocket;   import javax.net.ssl.SSLSocketFactory;     public class Simple_SSLSocket{    // 定義要連接的服務器名和端口號   private static final int DEFAULT_PORT=54321;    private static final String DEFAULT_HOST="localhost";    public static void main(String args[]){      SSLSocket socket=null;      // 使用默認的方式獲取工廠實例     SSLSocketFactory sf=(SSLSocketFactory)SSLSocketFactory.getDefault();     try{        // 連接服務端的端口,完成握手過程       socket=(SSLSocket)sf.createSocket(DEFAULT_HOST, DEFAULT_PORT);        socket.startHandshake();        System.out.println("Connected to "+DEFAULT_HOST+":"+DEFAULT_PORT+" !");        // 從控制臺輸入要發送給服務端的文字       BufferedReader reader=new BufferedReader(new InputStreamReader(System.in));        Writer writer=new OutputStreamWriter(socket.getOutputStream());    // 可以反復向服務端發送消息       boolean done=false;        while (!done) {          System.out.print("Send Message: ");          String line=reader.readLine();          if (line!=null) {            writer.write(line+"\n");            writer.flush();          }else{            done=true;          }        }        socket.close();      }catch (Exception e) {        System.out.println("Connection failed: "+e);        try{          socket.close();        }catch(IOException ioe){}        socket=null;      }    }   }

通過 SSLContext 指定證書庫和信任庫

前文描述的,通過系統參數指定證書庫和信任庫的方法,雖然簡單易用,但是缺點也是顯而易見的,整個程序的環境都得使用同樣的 jks 文件。如果程序里有不同的 SSL/TSL         通信,則需要使用不同的 jks 文件,該怎么做呢?

可以使用 SSLContext 來指定 jks 文件,只需要把清單 3 的代碼片段替換到清單 1 的“SSLServerSocketFactory ssf”生成處;把清單 4         的代碼片段替換到清單 2 的“SSLSocketFactory sf”生成處,再稍作代碼調整即可。

(注:實際上,在使用 SSLSocketFactory.getDefault() 或者         SSLServerSocketFactory.getDefault() 創建套接字的時候,程序內部已經使用了默認的 context,其參數就是通過系統屬性指定的 )

清單 3. SSLContext 指定證書庫

// 相關的 jks 文件及其密碼定義  private final static String CERT_STORE="D:/test_server_cert.jks";   private final static String CERT_STORE_PASSWORD="Testpassw0rd";  // 載入 jks 文件  FileInputStream f_certStore=new FileInputStream(CERT_STORE);   KeyStore ks=KeyStore.getInstance("jks");   ks.load(f_certStore, CERT_STORE_PASSWORD.toCharArray());   f_certStore.close();    // 創建并初始化證書庫工廠  String alg=KeyManagerFactory.getDefaultAlgorithm();   KeyManagerFactory kmFact=KeyManagerFactory.getInstance(alg);   kmFact.init(ks, CERT_STORE_PASSWORD.toCharArray());    KeyManager[] kms=kmFact.getKeyManagers();    // 創建并初始化 SSLContext 實例  SSLContext context=SSLContext.getInstance("SSL");   context.init(kms, null, null);   SSLServerSocketFactory ssf=(SSLServerSocketFactory)context.getServerSocketFactory();

清單 4. SSLContext 指定信任庫

// 相關的 jks 文件及其密碼定義  private final static String TRUST_STORE="D:/test_client_trust.jks";   private final static String TRUST_STORE_PASSWORD="Testpassw0rd";  // 載入 jks 文件  FileInputStream f_trustStore=new FileInputStream(TRUST_STORE);   KeyStore ks=KeyStore.getInstance("jks");   ks.load(f_trustStore, TRUST_STORE_PASSWORD.toCharArray());   f_trustStore.close();    // 創建并初始化信任庫工廠  String alg=TrustManagerFactory.getDefaultAlgorithm();   TrustManagerFactory tmFact=TrustManagerFactory.getInstance(alg);   tmFact.init(ks);    TrustManager[] tms=tmFact.getTrustManagers();    // 創建并初始化 SSLContext 實例  SSLContext context=SSLContext.getInstance("SSL");   context.init(null, tms, null);   SSLSocketFactory sf=context.getSocketFactory();

當然,如果要在服務端或者客戶端同時使用證書庫和信任庫,可將清單 3 和清單 4 的代碼用在同一處 context.init() 的地方。

這里需要說明的是,如果 context.init() 的第一個 KeyManager[] 參數為 null,則表示不提供證書;如果第二個 TrustManager[] 參數為         null,則會尋找系統默認提供的信任庫 ( 例如:JRE 安裝目錄下的 lib/security/cacerts)。

使用 X509 證書信任管理器

X509TrustManager 接口擴展了 TrustManager 接口,使用 TrustManager         接口,我們已經可以在程序中自定義信任庫了,但如果對方的證書不在信任庫中,則通信會直接宣告失敗。

如果希望能自定義信任庫的一些行為 ( 例如:檢驗對方證書,針對異常做一些處理 ),我們可以使用 X509TrustManager 接口,實現自己的方法。

如清單 5 所示,假定我們要在客戶端程序使用 X509TrustManager,那么就可以在 checkServerTrusted()         函數里做一些事情,檢測到服務端證書異常的話,就可以做一些自己的處理。CheckClientTrusted() 則是用于服務端檢測客戶端的證書情況。

將清單 5 的代碼替換到清單 4 的 TrustManager[] tms 的生成處,并對代碼稍作調整即可。

清單 5. X509TrustManager 的使用

// 使用自定義的 MyTrustManager 產生信任庫  TrustManager[] tms=new TrustManager[]{new MyTrustManager()};  &hellip;&hellip; &hellip;&hellip;  class MyTrustManager implements X509TrustManager{    // 相關的 jks 文件及其密碼定義   private final static String TRUST_STORE="D:/test_client_trust.jks";    private final static String TRUST_STORE_PASSWORD="Testpassw0rd";    X509TrustManager xtm;      public MyTrustManager() throws Exception {      // 載入 jks 文件     KeyStore ks = KeyStore.getInstance("JKS");      ks.load(new FileInputStream(TRUST_STORE),TRUST_STORE_PASSWORD.toCharArray());      TrustManagerFactory tmf =TrustManagerFactory.getInstance("SunX509", "SunJSSE");      tmf.init(ks);     TrustManager[] tms = tmf.getTrustManagers();     // 篩選出 X509 格式的信任證書     for (int i = 0; i < tms.length; i++) {        if (tms[i] instanceof X509TrustManager) {          xtm = (X509TrustManager) tms[i];          return;        }      }    }   // 服務端檢驗客戶端證書的接口   public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException{    }  // 客戶端檢驗服務端證書的接口   public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException{      try{        xtm.checkServerTrusted(chain, authType);      }catch(CertificateException excep){        System.out.println(excep.getMessage());        throw excep;      }    }   // 獲取可接受的發行者   public X509Certificate[] getAcceptedIssuers() {     //return xtm.getAcceptedIssuers();      return null;    }   }

注意:

1. 當服務端代碼 setNeedClientAuth(False) 時,客戶端的 MyTrustManager 實現了 X509TrustManager 后,如果         checkServerTrusted() 方法的實現為空,則無論服務端使用什么證書,客戶端都會默認接受;如果要對服務端證書進行檢查,還需要像清單 5         中的代碼片段那樣,捕捉異常并處理。

2.getAcceptedIssuers() 方法通常不需要具體實現,但是當服務端要求檢驗客戶端身份,也即 setNeedClientAuth(True) 時,服務端需也需要具體實現         X509TrustManager,且 getAcceptedIssuers() 方法要如清單 5 中注釋部分代碼那樣實現。

調試 SSL/TSL 程序

打開調試開關觀察通信日志

圖 2 描述了 SSL/TSL 通信的握手過程。在實際編寫程序的時候,可能會在這些環節遇到問題,導致無法通信,排查起來往往令人無從下手。這個時候我們可以將 SSL/TSL         通信的握手日志開關打開,進行觀察。

圖 2.SSL 通信協議握手過程

Java安全套接字編程以及keytool使用最佳實踐分析

(注:圖片引自 《Java&trade; Secure Socket Extension (JSSE) Reference Guide》)

打開日志開關的方法同樣是通過設定系統屬性,可以從命令行添加虛擬機參數:

-Djavax.net.debug=ssl,handshake

當然也可以使用 System.setProperty() 方法在代碼中打開該開關。

打開日志開關后,可以搜索“ClientHello”、“ServerHello”等關鍵字;或者搜索“*** ”( 三個星號和一個空格 )         &mdash;&mdash;這是握手階段每一個步驟日志打印的開始標志。通過閱讀日志來定位問題。更詳細的開關信息,請參閱 JSSE 指導中的“Debugging Utilities”章節:

選擇通信的 Cipher Suites

有的時候為了做實驗,我們會選用特定的 Cipher Suites,我們可以使用 SSLServerSocket 或 SSLSocket 的         getEnabledCipherSuites() 觀察到默認支持的所有加密套件的信息,然后使用 setEnabledCipherSuites() 進行設置。

清單 6 展示了從服務端過濾掉所有的匿名加密套件的代碼。

清單 6. 過濾所有的匿名加密套件

String enabled[]=serverSocket.getEnabledCipherSuites();  Set<String> filter = new LinkedHashSet<String>();  for(int i = 0; i < enabled.length; i++) {  if(enabled[i].indexOf("anon")<0){  filter.add(enabled[i]);  }  }  serverSocket.setEnabledCipherSuites(filter.toArray(new String[filter.size()]));

關于Java安全套接字編程以及keytool使用最佳實踐分析就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

保定市| 达尔| 水城县| 九龙坡区| 南皮县| 淮南市| 大庆市| 巴东县| 雷州市| 巴彦淖尔市| 开远市| 南昌市| 宜兰市| 岳阳县| 广宗县| 峡江县| 应用必备| 临潭县| 凤台县| 北碚区| 平山县| 漳浦县| 建水县| 满城县| 乡宁县| 新昌县| 南漳县| 辉县市| 鹤壁市| 章丘市| 长沙市| 普宁市| 海口市| 璧山县| 绍兴市| 巩留县| 疏勒县| 奉节县| 义乌市| 大足县| 洮南市|