欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java通過SSLEngine與NIO實(shí)現(xiàn)HTTPS訪問的操作方法

 更新時(shí)間:2021年08月21日 09:05:02   作者:爆走de蘿卜  
這篇文章主要介紹了Java通過SSLEngine與NIO實(shí)現(xiàn)HTTPS訪問,需要在Connect操作、Connected操作、Read和Write操作中加入SSL相關(guān)的處理即可,需要的朋友可以參考下

Java使用NIO進(jìn)行HTTPS協(xié)議訪問的時(shí)候,離不開SSLContext和SSLEngine兩個(gè)類。我們只需要在Connect操作、Connected操作、Read和Write操作中加入SSL相關(guān)的處理即可。

一、連接服務(wù)器之前先初始化SSLContext并設(shè)置證書相關(guān)的操作。

public void Connect(String host, int port) {
     mSSLContext = this.InitSSLContext();
     super.Connect(host, port);  
 }

在連接服務(wù)器前先創(chuàng)建SSLContext對(duì)象,并進(jìn)行證書相關(guān)的設(shè)置。如果服務(wù)器不是使用外部公認(rèn)的認(rèn)證機(jī)構(gòu)生成的密鑰,可以使用基于公鑰CA的方式進(jìn)行設(shè)置證書。如果是公認(rèn)的認(rèn)證證書一般只需要加載Java KeyStore即可。

1.1 基于公鑰CA

public SSLContext InitSSLContext() throws NoSuchAlgorithmException{
  // 創(chuàng)建生成x509證書的對(duì)象
  CertificateFactory caf = CertificateFactory.getInstance("X.509");
  // 這里的CA_PATH是服務(wù)器的ca證書,可以通過瀏覽器保存Cer證書(Base64和DER都可以)
  X509Certificate ca = (X509Certificate)caf.generateCertificate(new FileInputStream(CA_PATH));
  KeyStore caKs = KeyStore.getInstance("JKS");
  caKs.load(null, null);
  // 將上面創(chuàng)建好的證書設(shè)置到倉庫里面,前面的`baidu-ca`只是一個(gè)別名可以任意不要出現(xiàn)重復(fù)即可。
  caKs.setCertificateEntry("baidu-ca", ca);
  TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
      tmf.init(caKs);
  // 最后創(chuàng)建SSLContext,將可信任證書列表傳入。
  SSLContext context = SSLContext.getInstance("TLSv1.2");
  context.init(null, tmf.getTrustManagers(), null);
  return context;
}

1.2 加載Java KeyStore

public SSLContext InitSSLContext() throws NoSuchAlgorithmException{
  // 加載java keystore 倉庫
  KeyStore caKs = KeyStore.getInstance("JKS");
  // 把生成好的jks證書加載進(jìn)來
  caKs.load(new FileInputStream(CA_PATH), PASSWORD.toCharArray());
  // 把加載好的證書放入信任的列表
  TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
  tmf.init(caKs);
  // 最后創(chuàng)建SSLContext,將可信任證書列表傳入。
  SSLContext context = SSLContext.getInstance("TLSv1.2");
  context.init(null, tmf.getTrustManagers(), null);
  return context;
}

二、連接服務(wù)器成功后,需要?jiǎng)?chuàng)建SSLEngine對(duì)象,并進(jìn)行相關(guān)設(shè)置與握手處理。

通過第一步生成的SSLContext創(chuàng)建SSLSocketFactory并將當(dāng)前的SocketChannel進(jìn)行綁定(注:很多別人的例子都沒有這步操作,如果只存在一個(gè)HTTPS的連接理論上沒有問題,但如果希望同時(shí)創(chuàng)建大量的HTTPS請(qǐng)求“可能”有問題,因?yàn)镾SLEngine內(nèi)部使用哪個(gè)Socket進(jìn)行操作數(shù)據(jù)是不確定,如果我的理解有誤歡迎指正)。

然后調(diào)用創(chuàng)建SSLEngine對(duì)象,并初始化操作數(shù)據(jù)的Buffer,然后開始進(jìn)入握手階段。(注:這里創(chuàng)建的Buffer主要用于將應(yīng)用層數(shù)據(jù)加密為網(wǎng)絡(luò)數(shù)據(jù),將網(wǎng)絡(luò)數(shù)據(jù)解密為應(yīng)用層數(shù)據(jù)使用:“密文與明文”)。

public final void OnConnected() {
  super.OnConnected();
  // 設(shè)置socket,并創(chuàng)建SSLEngine,開始握手
  SSLSocketFactory fx = mSSLContext.getSocketFactory();
  // 這里將自己的channel傳進(jìn)去
  fx.createSocket(mSocketChannel.GetSocket(), mHost, mPort, false);
  mSSLEngine = this.InitSSLEngine(mSSLContext);
  // 初始化使用的BUFFER
  int appBufSize = mSSLEngine.getSession().getApplicationBufferSize();
  int netBufSize = mSSLEngine.getSession().getPacketBufferSize();
  mAppDataBuf = ByteBuffer.allocate(appBufSize);
  mNetDataBuf = ByteBuffer.allocate(netBufSize);
  pAppDataBuf = ByteBuffer.allocate(appBufSize);
  pNetDataBuf = ByteBuffer.allocate(netBufSize);
  // 初始化完成,準(zhǔn)備開啟握手
  mSSLInitiated = true;
  mSSLEngine.beginHandshake();
  this.ProcessHandShake(null);
}

三、進(jìn)行握手操作

下圖簡(jiǎn)單展示了握手流程,由客戶端發(fā)起,通過一些列的數(shù)據(jù)交換最終完成握手操作。要成功與服務(wù)器建立連接,握手流程是非常重要的環(huán)節(jié),幸好SSEngine內(nèi)部已經(jīng)實(shí)現(xiàn)了證書驗(yàn)證、交換等步驟,我們只需要在其上層執(zhí)行特定的行為(握手狀態(tài)處理)。

3.1 握手相關(guān)狀態(tài)(來自getHandshakeStatus方法)

NEED_WRAP當(dāng)前握手狀態(tài)表示需要加密數(shù)據(jù),即將要發(fā)送的應(yīng)用層數(shù)據(jù)加密輸出為網(wǎng)絡(luò)層數(shù)據(jù),并執(zhí)行發(fā)送操作。

NEED_UNWRAP當(dāng)前握手狀態(tài)表示需要對(duì)數(shù)據(jù)進(jìn)行解密,即將收到的網(wǎng)絡(luò)層數(shù)據(jù)解密后成應(yīng)用層數(shù)據(jù)。

NEED_TASK當(dāng)前握手狀態(tài)表示需要執(zhí)行任務(wù),因?yàn)橛行┎僮骺赡鼙容^耗時(shí),如果不希望造成阻塞流程就需要開啟異步任務(wù)進(jìn)行執(zhí)行。

FINISHED當(dāng)前握手已完成

NOT_HANDSHAKING表示不需要握手,這個(gè)主要是再次連接時(shí),為了加快速度而跳過握手流程。

3.2處理握手的方法

以下代碼展示了握手流程中的各種狀態(tài)的處理,主要的邏輯就是如果需要加密就執(zhí)行加密操作,如果需要執(zhí)行解密就執(zhí)行解密操作(廢話@_@!)。

protected void ProcessHandShake(SSLEngineResult result){
 if(this.isClosed() || this.isShutdown()) return;
 // 區(qū)分是來此WRAP UNWRAP調(diào)用,還是其他調(diào)用
 SSLEngineResult.HandshakeStatus status;
 if(result != null){
  status = result.getHandshakeStatus();
 }else{
  status = mSSLEngine.getHandshakeStatus();
 }
 switch(status)
 {
  // 需要加密
  case NEED_WRAP:
      //判斷isOutboundDone,當(dāng)true時(shí),說明已經(jīng)不需要再處理任何的NEED_WRAP操作了.
      // 因?yàn)橐呀?jīng)顯式調(diào)用過closeOutbound,且就算執(zhí)行wrap,
      // SSLEngineReulst.STATUS也一定是CLOSED,沒有任何意義
      if(mSSLEngine.isOutboundDone()){
        // 如果還有數(shù)據(jù)則發(fā)送出去
        if(mNetDataBuf.position() > 0) {
            mNetDataBuf.flip();
            mSocketChannel.WriteAndFlush(mNetDataBuf);
        }
        break;
      }
      // 執(zhí)行加密流程
      this.ProcessWrapEvent();
      break;
  // 需要解密
  case NEED_UNWRAP:
   //判斷inboundDone是否為true, true說明peer端發(fā)送了close_notify,
   // peer發(fā)送了close_notify也可能被unwrap操作捕獲到,結(jié)果就是返回的CLOSED
   if(mSSLEngine.isInboundDone()){
    //peer端發(fā)送關(guān)閉,此時(shí)需要判斷是否調(diào)用closeOutbound
    if(mSSLEngine.isOutboundDone()){
     return;
    }
    mSSLEngine.closeOutbound();
   }
   break;
  case NEED_TASK:
   // 執(zhí)行異步任務(wù),我這里是同步執(zhí)行的,可以弄一個(gè)異步線程池進(jìn)行。
   Runnable task = mSSLEngine.getDelegatedTask();
   if(task != null){
    task.run();
    // executor.execute(task); 這樣使用異步也是可以的,
    //但是異步就需要對(duì)ProcessHandShake的調(diào)用做特殊處理,因?yàn)楫惒降?,像下面這直接是會(huì)導(dǎo)致瘋狂調(diào)用。
   }
   this.ProcessHandShake(null);  // 繼續(xù)處理握手
   break;
  case FINISHED:
   // 握手完成
   mHandshakeCompleted = true;
   this.OnHandCompleted();
   return;
  case NOT_HANDSHAKING:
   // 不需要握手
   if(!mHandshakeCompleted)
   {
    mHandshakeCompleted = true;
    this.OnHandCompleted();
   }
   return;
 }
}

四、數(shù)據(jù)的發(fā)送與接收

握手成功后就可以進(jìn)行正常的數(shù)據(jù)發(fā)送與接收,但是需要額外在數(shù)據(jù)發(fā)送的時(shí)候進(jìn)行加密操作,數(shù)據(jù)接收后進(jìn)行解密操作。

這里需要額外說明一下,在握手期間也是會(huì)需要讀取數(shù)據(jù)的,因?yàn)榉?wù)器發(fā)送過來的數(shù)據(jù)需要我們執(zhí)行讀取并解密操作。而這個(gè)操作在一些其他的例子中直接使用了阻塞的讀取方式,我這里則是放在OnRead事件調(diào)用后進(jìn)行處理,這樣才符合NIO模型。

4.1加密操作(SelectionKey.OP_WRITE)

protected void ProcessWrapEvent(){
 if(this.isClosed() || this.isShutdown()) return;
 SSLEngineResult result = mSSLEngine.wrap(mAppDataBuf, mNetDataBuf);
 // 處理result
 if(ProcessSSLStatus(result, true)){
  mNetDataBuf.flip();
  mSocketChannel.WriteAndFlush(mNetDataBuf);
  // 發(fā)完成后清空buffer
  mNetDataBuf.clear();
 }
 mAppDataBuf.clear();
 // 如果沒有握手完成,則繼續(xù)調(diào)用握手處理
 if(!mHandshakeCompleted)
   this.ProcessHandShake(result);
}

4.2 解密操作(SelectionKey.OP_READ)

protected void ProcessUnWrapEvent(){
 if(this.isClosed() || this.isShutdown()) return;
 do{
  // 執(zhí)行解密操作
  SSLEngineResult res = mSSLEngine.unwrap(pNetDataBuf, pAppDataBuf);
  if(!ProcessSSLStatus(res, false))
      // 這里不需要對(duì)`pNetDataBuf`進(jìn)行處理,因?yàn)镻rocessSSLStatus里面已經(jīng)做好處理了。
   return;
  if(res.getStatus() == Status.CLOSED)
   break;
  // 未完成握手時(shí),需要繼續(xù)調(diào)用握手處理
  if(!mHandshakeCompleted)
   this.ProcessHandShake(res);
 }while(pNetDataBuf.hasRemaining());
 // 數(shù)據(jù)都解密完了,這個(gè)就可以清空了。
 if(!pNetDataBuf.hasRemaining())
   pNetDataBuf.clear();
}

到此這篇關(guān)于Java通過SSLEngine與NIO實(shí)現(xiàn)HTTPS訪問的文章就介紹到這了,更多相關(guān)Java通過SSLEngine與NIO實(shí)現(xiàn)HTTPS訪問內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java中Lock鎖基本使用方法詳解

    Java中Lock鎖基本使用方法詳解

    鎖是一種工具,用于控制對(duì)共享資源的訪問Lock和synchronized是最常見的兩個(gè)鎖,他們都能夠達(dá)到線程安全的目錄,這篇文章主要給大家介紹了關(guān)于Java中Lock鎖基本使用方法詳解的相關(guān)資料,需要的朋友可以參考下
    2023-11-11
  • Netty通道的容器屬性Attribute詳解

    Netty通道的容器屬性Attribute詳解

    這篇文章主要介紹了Netty通道的容器屬性Attribute詳解,Netty中的Channel通道類,有類似于Map的容器功能,可以通過鍵值對(duì)的形式來保存任何Java Object的值,一般來說可以存放一些與通道實(shí)例相關(guān)聯(lián)的屬性,比如說服務(wù)期端的ServerSession會(huì)話實(shí)例,需要的朋友可以參考下
    2023-12-12
  • IDEA進(jìn)程已結(jié)束,退出代碼-1073741819 (0xC0000005)的bug

    IDEA進(jìn)程已結(jié)束,退出代碼-1073741819 (0xC0000005)的bug

    這篇文章主要介紹了IDEA進(jìn)程已結(jié)束,退出代碼-1073741819 (0xC0000005)的bug,本文通過實(shí)例代碼圖文的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-04-04
  • 基于Spring MVC 簡(jiǎn)介及入門小例子(推薦)

    基于Spring MVC 簡(jiǎn)介及入門小例子(推薦)

    下面小編就為大家?guī)硪黄赟pring MVC 簡(jiǎn)介及入門小例子(推薦)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-06-06
  • MybatisPlus中的save方法詳解

    MybatisPlus中的save方法詳解

    save方法是Mybatis-plus框架提供的一個(gè)添加記錄的方法,它用于將一個(gè)實(shí)體對(duì)象插入到數(shù)據(jù)庫表中,這篇文章主要介紹了MybatisPlus中的save方法,需要的朋友可以參考下
    2023-11-11
  • 淺析Java 反射機(jī)制的用途和缺點(diǎn)

    淺析Java 反射機(jī)制的用途和缺點(diǎn)

    這篇文章給大家分析了Java 反射機(jī)制的用途和缺點(diǎn)以及相關(guān)知識(shí)點(diǎn)內(nèi)容,有興趣的朋友可以參考學(xué)習(xí)下。
    2018-07-07
  • Java靜態(tài)泛型使用方法實(shí)例解析

    Java靜態(tài)泛型使用方法實(shí)例解析

    這篇文章主要介紹了Java靜態(tài)泛型使用方法實(shí)例解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-06-06
  • java 線程池封裝及拒絕策略示例詳解

    java 線程池封裝及拒絕策略示例詳解

    這篇文章主要為大家介紹了java 線程池封裝及拒絕策略示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • Spring通過Java配置集成Tomcat的方法

    Spring通過Java配置集成Tomcat的方法

    這篇文章主要介紹了Spring通過Java配置集成Tomcat的方法,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-04-04
  • java線程池prestartCoreThread prestartAllCoreThreads的預(yù)熱源碼解讀

    java線程池prestartCoreThread prestartAllCoreThreads的預(yù)熱源碼解讀

    這篇文章主要介紹了java線程池prestartCoreThread prestartAllCoreThreads的預(yù)熱源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-10-10

最新評(píng)論