最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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è)置到倉(cāng)庫(kù)里面,前面的`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 倉(cāng)庫(kù)
  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)文章

  • Spring Boot Event Bus用法小結(jié)

    Spring Boot Event Bus用法小結(jié)

    Spring Boot Event Bus是Spring框架中事件驅(qū)動(dòng)編程的一部分,本文主要介紹了Spring Boot Event Bus用法小結(jié),感興趣的可以了解一下
    2023-09-09
  • springBoot下實(shí)現(xiàn)java自動(dòng)創(chuàng)建數(shù)據(jù)庫(kù)表

    springBoot下實(shí)現(xiàn)java自動(dòng)創(chuàng)建數(shù)據(jù)庫(kù)表

    這篇文章主要介紹了springBoot下實(shí)現(xiàn)java自動(dòng)創(chuàng)建數(shù)據(jù)庫(kù)表的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • Windows系統(tǒng)下Eclipse搭建ESP32編譯環(huán)境及安裝過程

    Windows系統(tǒng)下Eclipse搭建ESP32編譯環(huán)境及安裝過程

    Ecppse 使用了 ESP-IDF 中的 Makefile 支持。這意味著您需要從創(chuàng)建 ESP-IDF 項(xiàng)目開始。您可以使用 github 中的 idf-template 項(xiàng)目,接下來通過本文給大家介紹Windows系統(tǒng)下Eclipse搭建ESP32編譯環(huán)境及安裝過程,感興趣的朋友一起看看吧
    2021-10-10
  • Java中BigDecimal的基本運(yùn)算(詳解)

    Java中BigDecimal的基本運(yùn)算(詳解)

    下面小編就為大家?guī)硪黄狫ava中BigDecimal的基本運(yùn)算(詳解)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-10-10
  • SpringCloud Netfilx Ribbon負(fù)載均衡工具使用方法介紹

    SpringCloud Netfilx Ribbon負(fù)載均衡工具使用方法介紹

    Ribbon是Netflix的組件之一,負(fù)責(zé)注冊(cè)中心的負(fù)載均衡,有助于控制HTTP和TCP客戶端行為。Spring Cloud Netflix Ribbon一般配合Ribbon進(jìn)行使用,利用在Eureka中讀取的服務(wù)信息,在調(diào)用服務(wù)節(jié)點(diǎn)時(shí)合理進(jìn)行負(fù)載
    2022-12-12
  • Hibernate基于ThreadLocal管理Session過程解析

    Hibernate基于ThreadLocal管理Session過程解析

    這篇文章主要介紹了Hibernate基于ThreadLocal管理Session過程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-10-10
  • 模擬Ping操作的一個(gè)Java類

    模擬Ping操作的一個(gè)Java類

    這篇文章主要為大家詳細(xì)介紹了一個(gè)模擬Ping操作的Java類,感興趣的小伙伴們可以參考一下
    2016-03-03
  • Java中notify是順序喚醒還是隨機(jī)喚醒的

    Java中notify是順序喚醒還是隨機(jī)喚醒的

    這篇文章主要介紹了Java中notify是順序喚醒還是隨機(jī)喚醒的,有很多人會(huì)認(rèn)為?notify?是隨機(jī)喚醒的,但它真的是隨機(jī)喚醒的嗎?帶著疑問一起進(jìn)入文章了解具體的內(nèi)容吧
    2022-05-05
  • SpringBoot多controller添加URL前綴的實(shí)現(xiàn)方法

    SpringBoot多controller添加URL前綴的實(shí)現(xiàn)方法

    這篇文章主要介紹了SpringBoot多controller添加URL前綴的方法,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-02-02
  • java awt生成簽名圖片如何消除鋸齒化

    java awt生成簽名圖片如何消除鋸齒化

    這篇文章主要介紹了java awt生成簽名圖片如何消除鋸齒化,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11

最新評(píng)論

新巴尔虎右旗| 尼玛县| 克拉玛依市| 浦江县| 耒阳市| 榆林市| 高青县| 新田县| 慈溪市| 灯塔市| 武邑县| 任丘市| 岳阳市| 揭东县| 江口县| 甘孜县| 巴彦县| 西丰县| 甘洛县| 枣强县| 原阳县| 双鸭山市| 余姚市| 白朗县| 安图县| 临城县| 贺兰县| 上林县| 银川市| 郎溪县| 中超| 呈贡县| 竹山县| 安宁市| 浙江省| 龙口市| 新宁县| 勐海县| 逊克县| 察雅县| 宾川县|