如何讓你的傳輸更安全——NIO 模式和 BIO 模式實現 SSL 協議通訊

對於SSL/TLS協議,若是要每一個開發者都本身去實現顯然會帶來沒必要要的麻煩,正是爲了解決這個問題Java爲廣大開發者提供了Java安全套接字擴展——JSSE,它包含了實現Internet安全通訊的一系列包的集合,是SSL和TLS的純Java實現,同時它是一個開放的標準,每一個公司均可以本身實現JSSE,經過它能夠透明地提供數據加密、服務器認證、信息完整性等功能,就像使用普通的套接字同樣使用安全套接字,大大減輕了開發者的負擔,使開發者能夠很輕鬆將SSL協議整合到程序中,而且JSSE能將安全隱患降到了最低點。javascript

在用JSSE實現SSL通訊過程當中主要會遇到如下類和接口,因爲過程當中涉及到加解密、密鑰生成等運算的框架和實現,因此也會間接用到JCE包的一些類。如圖爲JSSE接口的主要類圖:html

① 通訊核心類——SSLSocket和SSLServerSocket。對於使用過socket進行通訊開發的朋友比較好理解,它們對應的就是Socket與ServerSocket,只是表示實現了SSL協議的Socket和ServerSocket,同時它們也是Socket與ServerSocket的子類。SSLSocket負責的事情包括設置加密套件、管理SSL會話、處理握手結束時間、設置客戶端模式或服務器模式。
② 客戶端與服務器端Socket工廠——SSLSocketFactory和SSLServerSocketFactory。在設計模式中工廠模式是專門用於生產出須要的實例,這裏也是把SSLSocket、SSLServerSocket對象建立的工做交給這兩個工廠類。
③ SSL會話——SSLSession。安全通訊握手過程須要一個會話,爲了提升通訊的效率,SSL協議容許多個SSLSocket共享同一個SSL會話,在同一個會話中,只有第一個打開的SSLSocket須要進行SSL握手,負責生成密鑰及交換密鑰,其他SSLSocket都共享密鑰信息。
④ SSL上下文——SSLContext。它是對整個SSL/TLS協議的封裝,表示了安全套接字協議的實現。主要負責設置安全通訊過程當中的各類信息,例如跟證書相關的信息。而且負責構建SSLSocketFactory、SSLServerSocketFactory和SSLEngine等工廠類。
⑤ SSL非阻塞引擎——SSLEngine。假如你要進行NIO通訊,那麼將使用這個類,它讓經過過程支持非阻塞的安全通訊。
⑥ 密鑰管理器——KeyManager。此接口負責選擇用於證明本身身份的安全證書,發給通訊另外一方。KeyManager對象由KeyManagerFactory工廠類生成。
⑦ 信任管理器——TrustManager。此接口負責判斷決定是否信任對方的安全證書,TrustManager對象由TrustManagerFactory工廠類生成。
⑧ 密鑰證書存儲設施——KeyStore。這個對象用於存放安全證書,安全證書通常以文件形式存放,KeyStore負責將證書加載到內存。java

經過上面這些類就能夠完成SSL協議的安全通訊了,在利用SSL/TLS進行安全通訊時,客戶端跟服務器端都必需要支持SSL/TLS協議,否則將沒法進行通訊。並且客戶端和服務器端均可能要設置用於證明本身身份的安全證書,而且還要設置信任對方的哪些安全證書。算法

關於身份認證方面有個名詞叫客戶端模式,通常狀況客戶端要對服務器端的身份進行驗證,可是無需向服務器證明本身的身份,這樣不用向對方證明本身身份的通訊端咱們就說它處於客戶模式,不然成它處於服務器模式。SSLSocket的setUseClientMode(Boolean mode)方法能夠設置客戶端模式或服務器模式。設計模式

BIO模式實現SSL通訊

使用BIO模式實現SSL通訊除了對一些證書密鑰生成外,只需使用JDK自帶的SSLServerSocket和SSLSocket等相關類的API便可實現,簡潔直觀。瀏覽器

① 解決證書問題。

通常而言做爲服務器端必需要有證書以證實這個服務器的身份,而且證書應該描述此服務器全部者的一些基本信息,例如公司名稱、聯繫人名等。證書由全部人以密碼形式簽名,基本不可僞造,證書獲取的途徑有兩個:一是從權威機構購買證書,權威機構擔保它發出的證書的真實性,並且這個權威機構被你們所信任,進而你能夠相信這個證書的有效性;另一個是本身用JDK提供的工具keytool建立一個自我簽名的證書,這種狀況下通常是我只想要保證數據的安全性與完整性,避免數據在傳送的過程當中被竊聽或篡改,此時身份的認證已不重要,重點已經在端與端傳輸的祕密性上,證書的做用只體如今加解密簽名。tomcat

另外,關於證書的一些概念在這裏陳述,一個證書是一個實體的數字簽名,這個實體能夠是一我的、一個組織、一個程序、一個公司、一個銀行,同時證書還包含這個實體的公共鑰匙,此公共鑰匙是這個實體的數字關聯,讓全部想同這個實體發生信任關係的其餘實體用來檢驗簽名。而這個實體的數字簽名是實體信息用實體的私鑰加密後的數據,這條數據能夠用這個實體的公共鑰匙解密,進而鑑別實體的身份。這裏用到的核心算法是非對稱加密算法。安全

SSL協議通訊涉及密鑰儲存的文件格式比較多,很容易搞混,例如xxx.cer、xxx.pfx、xxx.jks、xxx.keystore、xxx.truststore等格式文件。如圖,搞清楚他們有助於理解後面的程序,.cer格式文件俗稱證書,但這個證書中沒有私鑰,只包含了公鑰;.pfx格式文件也稱爲證書,它通常供瀏覽器使用,並且它不只包含了公鑰,還包含了私鑰,固然這個私鑰是加密的,不輸入密碼是解不了密的;.jks格式文件表示java密鑰存儲器(java key store),它能夠同時容納N個公鑰跟私鑰,是一個密鑰庫;.keystore格式文件其實跟.jks基本是同樣的,只是不一樣公司叫法不太同樣,默認生成的證書存儲庫格式;.truststore格式文件表示信任證書存儲庫,它僅僅包含了通訊對方的公鑰,固然你能夠直接把通訊對方的jks做爲信任庫(就算如此你也只能知道通訊對方的公鑰,要知道密鑰都是加密的,你無從獲取,只要算法不被破解)。有些時候咱們須要把pfx或cert轉化爲jks以便於用java進行ssl通訊,例如一個銀行只提供了pfx證書,而咱們想用java進行ssl通訊時就要將pfx轉化爲jks格式。服務器

按照理論上,咱們一共須要準備四個文件,兩個keystore文件和兩個truststore文件,通訊雙方分別擁有一個keystore和一個truststore,keystore用於存放本身的密鑰和公鑰,truststore用於存放全部須要信任方的公鑰。這裏爲了方便直接使用jks即keystore替代truststore(免去證書導來導去),由於對方的keystore包含了本身須要的信任公鑰。網絡

下面使用jdk自帶的工具分別生成服務器端證書,經過以下命令並輸入姓名、組織單位名稱、組織名稱、城市、省份、國家信息便可生成證書密碼爲tomcat的證書,此證書存放在密碼也爲tomcat的tomcat.jks證書存儲庫中。若是你繼續建立證書將繼續往tomcat.jks證書存儲庫中添加證書。若是你僅僅輸入keytool -genkey -alias tomcat -keyalg RSA -keypass tomcat -storepass tomcat,不指定證書存儲庫的文件名及路徑,則工具會在用戶的home directory目錄下生產一個「.keystore」文件做爲證書存儲庫。


相似的,客戶端證書也用此方式進行生成。以下

② 服務端TomcatSSLServer.java

public class TomcatSSLServer {
  private static final String SSL_TYPE = "SSL";
  private static final String KS_TYPE = "JKS";
  private static final String X509 = "SunX509";
  private final static int PORT = 443;
  private static TomcatSSLServer sslServer;
  private SSLServerSocket svrSocket;

  public static TomcatSSLServer getInstance() throws Exception {
    if (sslServer == null) {
      sslServer = new TomcatSSLServer();
    }
    return sslServer;
  }

  private TomcatSSLServer() throws Exception {
    SSLContext sslContext = createSSLContext();
    SSLServerSocketFactory serverFactory = sslContext.getServerSocketFactory();
    svrSocket = (SSLServerSocket) serverFactory.createServerSocket(PORT);
    svrSocket.setNeedClientAuth(true);
    String[] supported = svrSocket.getEnabledCipherSuites();
    svrSocket.setEnabledCipherSuites(supported);
  }

  private SSLContext createSSLContext() throws Exception {
    KeyManagerFactory kmf = KeyManagerFactory.getInstance(X509);
    TrustManagerFactory tmf = TrustManagerFactory.getInstance(X509);
    String serverKeyStoreFile = "c:\\tomcat.jks";
    String svrPassphrase = "tomcat";
    char[] svrPassword = svrPassphrase.toCharArray();
    KeyStore serverKeyStore = KeyStore.getInstance(KS_TYPE);
    serverKeyStore.load(new FileInputStream(serverKeyStoreFile), svrPassword);
    kmf.init(serverKeyStore, svrPassword);
    String clientKeyStoreFile = "c:\\client.jks";
    String cntPassphrase = "client";
    char[] cntPassword = cntPassphrase.toCharArray();
    KeyStore clientKeyStore = KeyStore.getInstance(KS_TYPE);
    clientKeyStore.load(new FileInputStream(clientKeyStoreFile), cntPassword);
    tmf.init(clientKeyStore);
    SSLContext sslContext = SSLContext.getInstance(SSL_TYPE);
    sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
    return sslContext;
  }

  public void startService() {
    SSLSocket cntSocket = null;
    BufferedReader ioReader = null;
    PrintWriter ioWriter = null;
    String tmpMsg = null;
    while (true) {
      try {
        cntSocket = (SSLSocket) svrSocket.accept();
        ioReader = new BufferedReader(new InputStreamReader(cntSocket.getInputStream()));
        ioWriter = new PrintWriter(cntSocket.getOutputStream());
        while ((tmpMsg = ioReader.readLine()) != null) {
          System.out.println("客戶端經過SSL協議發送信息:" + tmpMsg);
          tmpMsg = "歡迎經過SSL協議鏈接";
          ioWriter.println(tmpMsg);
          ioWriter.flush();
        }
      } catch (IOException e) {
        e.printStackTrace();
      } finally {
        try {
          if (cntSocket != null) cntSocket.close();
        } catch (Exception ex) {
          ex.printStackTrace();
        }
      }
    }
  }

  public static void main(String[] args) throws Exception {
    TomcatSSLServer.getInstance().startService();
  }
}複製代碼

基本順序是先獲得一個SSLContext實例,再對SSLContext實例進行初始化,密鑰管理器及信任管理器做爲參數傳入,證書管理器及信任管理器按照指定的密鑰存儲器路徑和密碼進行加載。接着設置支持的加密套件,最後讓SSLServerSocket開始監聽客戶端發送過來的消息。

③ 客戶端TomcatSSLClient.java

public class TomcatSSLClient {
  private static final String SSL_TYPE = "SSL";
  private static final String X509 = "SunX509";
  private static final String KS_TYPE = "JKS";
  private SSLSocket sslSocket;

  public TomcatSSLClient(String targetHost, int port) throws Exception {
    SSLContext sslContext = createSSLContext();
    SSLSocketFactory sslcntFactory = (SSLSocketFactory) sslContext.getSocketFactory();
    sslSocket = (SSLSocket) sslcntFactory.createSocket(targetHost, port);
    String[] supported = sslSocket.getSupportedCipherSuites();
    sslSocket.setEnabledCipherSuites(supported);
  }

  private SSLContext createSSLContext() throws Exception {
    KeyManagerFactory kmf = KeyManagerFactory.getInstance(X509);
    TrustManagerFactory tmf = TrustManagerFactory.getInstance(X509);
    String clientKeyStoreFile = "c:\\client.jks";
    String cntPassphrase = "client";
    char[] cntPassword = cntPassphrase.toCharArray();
    KeyStore clientKeyStore = KeyStore.getInstance(KS_TYPE);
    clientKeyStore.load(new FileInputStream(clientKeyStoreFile), cntPassword);
    String serverKeyStoreFile = "c:\\tomcat.jks";
    String svrPassphrase = "tomcat";
    char[] svrPassword = svrPassphrase.toCharArray();
    KeyStore serverKeyStore = KeyStore.getInstance(KS_TYPE);
    serverKeyStore.load(new FileInputStream(serverKeyStoreFile), svrPassword);
    kmf.init(clientKeyStore, cntPassword);
    tmf.init(serverKeyStore);
    SSLContext sslContext = SSLContext.getInstance(SSL_TYPE);
    sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
    return sslContext;
  }

  public String sayToSvr(String sayMsg) throws IOException {
    BufferedReader ioReader = new BufferedReader(new InputStreamReader(sslSocket.getInputStream()));
    PrintWriter ioWriter = new PrintWriter(sslSocket.getOutputStream());
    ioWriter.println(sayMsg);
    ioWriter.flush();
    return ioReader.readLine();
  }

  public static void main(String[] args) throws Exception {
    TomcatSSLClient sslSocket = new TomcatSSLClient("127.0.0.1", 443);
    BufferedReader ioReader = new BufferedReader(new InputStreamReader(System.in));
    String sayMsg = "";
    String svrRespMsg = "";
    while ((sayMsg = ioReader.readLine()) != null) {
      svrRespMsg = sslSocket.sayToSvr(sayMsg);
      if (svrRespMsg != null && !svrRespMsg.trim().equals("")) {
        System.out.println("服務器經過SSL協議響應:" + svrRespMsg);
      }
    }
  }
}複製代碼

客戶端的前面操做基本跟服務器端的同樣,先建立一個SSLContext實例,再用密鑰管理器及信任管理器對SSLContext進行初始化,固然這裏密鑰存儲的路徑是指向客戶端的client.jks。接着設置加密套件,最後使用SSLSocket進行通訊。

注意服務器端有行代碼svrSocket.setNeedClientAuth(true);它是很是重要的一個設置方法,用於設置是否驗證客戶端的身份。假如咱們把它註釋掉或設置爲false,此時客戶端將再也不須要本身的密鑰管理器,即服務器不須要經過client.jks對客戶端的身份進行驗證,把密鑰管理器直接設置爲null也能夠跟服務器端進行通訊。

最後談談信任管理器,它的職責是決定是否信任遠端的證書,那麼它憑藉什麼去判斷呢?若是不顯式設置信任存儲器的文件路徑,將遵循以下規則:①若是系統屬性javax.NET.ssl.truststore指定了truststore文件,那麼信任管理器將去jre路徑下的lib/security目錄尋找這個文件做爲信任存儲器;②若是沒設置①中的系統屬性,則去尋找一個%java_home%/lib/security/jssecacerts文件做爲信任存儲器;③若是jssecacerts不存在而cacerts存在,則cacerts做爲信任存儲器。

至此,一個利用JSSE實現BIO模式的SSL協議通訊的例子已完成。

NIO模式實現SSL通訊

在jdk1.5以前,因爲互聯網還沒快速發展起來,對於常見的應用使用BIO模式便可知足需求,而這時jdk的JSSE接口也僅僅只是提供了基於流的安全套接字,但隨着網絡的發展,BIO模型明顯已經不足以知足一些高併發多鏈接接入的場景,體如今機器上就是要不一樣的線程模型以致於能最大程度地壓榨計算器的運算,因而此時引入了NIO模式,原來基於流的阻塞模式IO只需使用SSLServerSocket和SSLSocket便可完成SSL通訊,而JDK中對於NIO模式並無提供與之對應的「SSLServerSocketChannel」和「SSLSocketChannel」,這是由NIO模式決定的,很難設計一個「SSLServerSocketChannel」類與Selector交互,強行地引入將帶來更多的問題,這更像解決一個問題引入了三個問題,而且還會致使API更加複雜,另外Nio細節也不適合屏蔽,它應該由應用開發層去控制。全部的這些都決定了jdk不會也不能有NIO安全套接字。

jdk1.5爲了支持NIO模式的SSL通訊,引入了SSLEngine引擎,它負責了底層ssl協議的握手、加密、解密、關閉會話等等操做,根據前面SSL\TLS協議章節咱們知道SSL協議在握手階段會有十三個步驟,在握手過程當中不會有應用層的數據傳輸,只有在握手認證完成後雙方纔會進行應用層數據交換。大體把握手分爲四階段:
①客戶端發送hello消息;
②服務端響應hello消息且發送附帶的認證消息;
③客戶端向客戶端發送證書和其餘認證消息;
④完成握手。

SSLEngine在握手過程當中定義了五種HandshakeStatus狀態,【NEED_WRAP、NEED_UNWRAP、NEED_TASK、FINISHED、NOT_HANDSHAKING】,經過他們實現協議通訊過程當中狀態管理,按照四個階段其中的狀態是這樣轉換的,剛開始它的狀態爲NEED_UNWRAP,表示等待解包,讀取客戶端數據並解包後,把狀態置爲NEED_WRAP,表示等待打包,打包完向客戶端響應數據後狀態又重置爲NEED_UNWRAP,如此切換直至握手完成時狀態被置爲FINISHED,表示握手已經完成,此後狀態置爲NOT_HANDSHAKING,表示已經不在握手階段了。另外還有一個NEED_TASK狀態表示SSLEngine有額外的任務須要執行,並且這些任務都是比較耗時或者可能阻塞的,例如訪問密鑰文件、鏈接遠程證書認證服務、密鑰管理器使用何種認證方式做爲客戶端認證等等操做。爲了保證NIO特性,這些操做不能直接由當前線程操做,當前線程只會把狀態改成NEED_TASK,後面處理線程會交由其餘線程處理。

看看程序是如何使用nio模式進行ssl通訊的,主要看服務端如何實現。

public class NioSSLServer {
  private SSLEngine sslEngine;
  private Selector selector;
  private SSLContext sslContext;
  private ByteBuffer netInData;
  private ByteBuffer appInData;
  private ByteBuffer netOutData;
  private ByteBuffer appOutData;
  private static final String SSL_TYPE = "SSL";
  private static final String KS_TYPE = "JKS";
  private static final String X509 = "SunX509";
  private final static int PORT = 443;

  public void run() throws Exception {
    createServerSocket();
    createSSLContext();
    createSSLEngine();
    createBuffer();
    while (true) {
      selector.select();
      Iterator<SelectionKey> it = selector.selectedKeys().iterator();
      while (it.hasNext()) {
        SelectionKey selectionKey = it.next();
        it.remove();
        handleRequest(selectionKey);
      }
    }
  }

  private void createBuffer() {
    SSLSession session = sslEngine.getSession();
    appInData = ByteBuffer.allocate(session.getApplicationBufferSize());
    netInData = ByteBuffer.allocate(session.getPacketBufferSize());
    appOutData = ByteBuffer.wrap("Hello\n".getBytes());
    netOutData = ByteBuffer.allocate(session.getPacketBufferSize());
  }

  private void createSSLEngine() {
    sslEngine = sslContext.createSSLEngine();
    sslEngine.setUseClientMode(false);
  }

  private void createServerSocket() throws Exception {
    ServerSocketChannel serverChannel = ServerSocketChannel.open();
    serverChannel.configureBlocking(false);
    selector = Selector.open();
    ServerSocket serverSocket = serverChannel.socket();
    serverSocket.bind(new InetSocketAddress(PORT));
    serverChannel.register(selector, SelectionKey.OP_ACCEPT);
  }

  private void createSSLContext() throws Exception {
    KeyManagerFactory kmf = KeyManagerFactory.getInstance(X509);
    TrustManagerFactory tmf = TrustManagerFactory.getInstance(X509);
    String serverKeyStoreFile = "c:\\tomcat.jks";
    String svrPassphrase = "tomcat";
    char[] svrPassword = svrPassphrase.toCharArray();
    KeyStore serverKeyStore = KeyStore.getInstance(KS_TYPE);
    serverKeyStore.load(new FileInputStream(serverKeyStoreFile), svrPassword);
    kmf.init(serverKeyStore, svrPassword);
    String clientKeyStoreFile = "c:\\client.jks";
    String cntPassphrase = "client";
    char[] cntPassword = cntPassphrase.toCharArray();
    KeyStore clientKeyStore = KeyStore.getInstance(KS_TYPE);
    clientKeyStore.load(new FileInputStream(clientKeyStoreFile), cntPassword);
    tmf.init(clientKeyStore);
    sslContext = SSLContext.getInstance(SSL_TYPE);
    sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
  }

  private void handleRequest(SelectionKey key) throws Exception {
    if (key.isAcceptable()) {
      ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
      SocketChannel channel = ssc.accept();
      channel.configureBlocking(false);
      doHandShake(channel);
    } else if (key.isReadable()) {
      if (sslEngine.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING) {
        SocketChannel sc = (SocketChannel) key.channel();
        netInData.clear();
        appInData.clear();
        sc.read(netInData);
        netInData.flip();
        SSLEngineResult engineResult = sslEngine.unwrap(netInData, appInData);
        doTask();
        if (engineResult.getStatus() == SSLEngineResult.Status.OK) {
          appInData.flip();
          System.out.println(new String(appInData.array()));

        }
        sc.register(selector, SelectionKey.OP_WRITE);
      }
    } else if (key.isWritable()) {
      SocketChannel sc = (SocketChannel) key.channel();
      netOutData.clear();
      SSLEngineResult engineResult = sslEngine.wrap(appOutData, netOutData);
      doTask();
      netOutData.flip();
      while (netOutData.hasRemaining())
        sc.write(netOutData);
      sc.register(selector, SelectionKey.OP_READ);
    }
  }

  private void doHandShake(SocketChannel sc) throws IOException {
    boolean handshakeDone = false;
    sslEngine.beginHandshake();
    HandshakeStatus hsStatus = sslEngine.getHandshakeStatus();
    while (!handshakeDone) {
      switch (hsStatus) {
        case FINISHED:
          break;
        case NEED_TASK:
          hsStatus = doTask();
          break;
        case NEED_UNWRAP:
          netInData.clear();
          sc.read(netInData);
          netInData.flip();
          do {
            SSLEngineResult engineResult = sslEngine.unwrap(netInData, appInData);
            hsStatus = doTask();
          } while (hsStatus == SSLEngineResult.HandshakeStatus.NEED_UNWRAP
              && netInData.remaining() > 0);
          netInData.clear();
          break;
        case NEED_WRAP:
          SSLEngineResult engineResult = sslEngine.wrap(appOutData, netOutData);
          hsStatus = doTask();
          netOutData.flip();
          sc.write(netOutData);
          netOutData.clear();
          break;
        case NOT_HANDSHAKING:
          sc.configureBlocking(false);
          sc.register(selector, SelectionKey.OP_READ);
          handshakeDone = true;
          break;
      }
    }
  }

  private HandshakeStatus doTask() {
    Runnable task;
    while ((task = sslEngine.getDelegatedTask()) != null) {
      new Thread(task).start();
    }
    return sslEngine.getHandshakeStatus();
  }

  public static void main(String[] args) throws Exception {
    new NioSSLServer().run();
  }
}複製代碼

根據程序大體說明程序過程,
①建立用於非阻塞通訊的主要對象ServerSocketChannel和Selector、綁定端口、註冊接收事件;
②建立SSL上下文,此過程主要是根據前面建立好的密鑰存儲器tomcat.jks和client.jks去建立密鑰管理器和信任管理器,並經過密鑰管理器和信任管理器去初始化SSL上下文;
③建立SSL引擎,主要經過SSL上下文建立SSL引擎,並將它設爲不驗證客戶端身份;
④建立緩衝區,使用SSL協議通訊的過程當中涉及到四個緩衝區,以下圖,netInData表示實際從網絡接收到的字節流,它是包含了SSL協議和應用數據的字節流,經過SSLEngine引擎進行認證解密等處理後的應用可直接使用的數據則用appInData表示,一樣地,應用層要傳遞的數據爲appOutData,而通過SSLEngine引擎認證加密處理後放到網絡中傳輸的字節流則爲netOutData;
⑤接下去開始監聽處理客戶端的鏈接請求,一旦有可接受的鏈接則會先進行SSL協議握手,完成握手後才能進行傳輸,即對通道的讀寫操做。

握手操做是一個比較複雜的過程,必需要保證握手完成後才能進行應用層數據交換,因此這裏使用一個while循環不斷作握手操做直到完成。前面已經介紹了握手階段會有五種狀態,【NEED_WRAP、NEED_UNWRAP、NEED_TASK、FINISHED、NOT_HANDSHAKING】,因爲SSL協議握手的報文都由SSLEngine引擎自動生成,因此咱們只需對不一樣狀態作不一樣操做便可,例如,NEED_UNWRAP狀態則調用unwrap方法,NEED_WRAP則調用wrap方法,NEED_TASK則使用其餘線程處理委託任務,握手報文自動由這些方法完成,當握手完成後狀態則被置爲FINISHED,接着狀態變爲NOT_HANDSHAKING,表示已經不在握手階段了,已經能夠進行應用層通訊了,此時整個SSL握手結束。

應用層安全通訊過程其實也是靠SSLEngine引擎的unwrap和wrap方法對數據進行加解密而且對通訊雙方進行認證,例如應用層讀操做是將netInData和appInData傳入unwrap方法,處理後的appInData即爲應用須要的數據,而寫操做則是將appOutData和netOutData傳入wrap方法,處理後的netOutData即爲傳輸給對方的數據。

至此,經過在網絡與應用直接增長一個SSLEngine引擎層,則實現了安全通訊,而且使用了NIO模式讓服務端擁有更加出色的處理性能。

====廣告時間,可直接跳過====

鄙人的新書《Tomcat內核設計剖析》已經在京東預售了,有須要的朋友能夠到 item.jd.com/12185360.ht… 進行預約。感謝各位朋友。

=========================

歡迎關注:

這裏寫圖片描述
相關文章
相關標籤/搜索