溫馨提示×

溫馨提示×

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

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

Java怎么實現HTTPS訪問的操作

發(fā)布時間:2021-08-21 10:26:33 來源:億速云 閱讀:480 作者:chen 欄目:開發(fā)技術

這篇文章主要講解了“Java怎么實現HTTPS訪問的操作”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“Java怎么實現HTTPS訪問的操作”吧!

目錄
  • 一、連接服務器之前先初始化SSLContext并設置證書相關的操作。

    • 1.1 基于公鑰CA

    • 1.2 加載Java KeyStore

  • 二、連接服務器成功后,需要創(chuàng)建SSLEngine對象,并進行相關設置與握手處理。

    • 三、進行握手操作

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

      • 3.2處理握手的方法

    • 四、數據的發(fā)送與接收

      • 4.1加密操作(SelectionKey.OP_WRITE)

      • 4.2 解密操作(SelectionKey.OP_READ)

    Java使用NIO進行HTTPS協議訪問的時候,離不開SSLContext和SSLEngine兩個類。我們只需要在Connect操作、Connected操作、Read和Write操作中加入SSL相關的處理即可。

    一、連接服務器之前先初始化SSLContext并設置證書相關的操作。

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

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

    1.1 基于公鑰CA

    public SSLContext InitSSLContext() throws NoSuchAlgorithmException{
      // 創(chuàng)建生成x509證書的對象
      CertificateFactory caf = CertificateFactory.getInstance("X.509");
      // 這里的CA_PATH是服務器的ca證書,可以通過瀏覽器保存Cer證書(Base64和DER都可以)
      X509Certificate ca = (X509Certificate)caf.generateCertificate(new FileInputStream(CA_PATH));
      KeyStore caKs = KeyStore.getInstance("JKS");
      caKs.load(null, null);
      // 將上面創(chuàng)建好的證書設置到倉庫里面,前面的`baidu-ca`只是一個別名可以任意不要出現重復即可。
      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證書加載進來
      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;
    }

    二、連接服務器成功后,需要創(chuàng)建SSLEngine對象,并進行相關設置與握手處理。

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

    然后調用創(chuàng)建SSLEngine對象,并初始化操作數據的Buffer,然后開始進入握手階段。(注:這里創(chuàng)建的Buffer主要用于將應用層數據加密為網絡數據,將網絡數據解密為應用層數據使用:“密文與明文”)。

    public final void OnConnected() {
      super.OnConnected();
      // 設置socket,并創(chuàng)建SSLEngine,開始握手
      SSLSocketFactory fx = mSSLContext.getSocketFactory();
      // 這里將自己的channel傳進去
      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);
      // 初始化完成,準備開啟握手
      mSSLInitiated = true;
      mSSLEngine.beginHandshake();
      this.ProcessHandShake(null);
    }

    三、進行握手操作

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

    Java怎么實現HTTPS訪問的操作

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

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

    NEED_UNWRAP當前握手狀態(tài)表示需要對數據進行解密,即將收到的網絡層數據解密后成應用層數據。

    NEED_TASK當前握手狀態(tài)表示需要執(zhí)行任務,因為有些操作可能比較耗時,如果不希望造成阻塞流程就需要開啟異步任務進行執(zhí)行。

    FINISHED當前握手已完成

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

    3.2處理握手的方法

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

    protected void ProcessHandShake(SSLEngineResult result){
     if(this.isClosed() || this.isShutdown()) return;
     // 區(qū)分是來此WRAP UNWRAP調用,還是其他調用
     SSLEngineResult.HandshakeStatus status;
     if(result != null){
      status = result.getHandshakeStatus();
     }else{
      status = mSSLEngine.getHandshakeStatus();
     }
     switch(status)
     {
      // 需要加密
      case NEED_WRAP:
          //判斷isOutboundDone,當true時,說明已經不需要再處理任何的NEED_WRAP操作了.
          // 因為已經顯式調用過closeOutbound,且就算執(zhí)行wrap,
          // SSLEngineReulst.STATUS也一定是CLOSED,沒有任何意義
          if(mSSLEngine.isOutboundDone()){
            // 如果還有數據則發(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操作捕獲到,結果就是返回的CLOSED
       if(mSSLEngine.isInboundDone()){
        //peer端發(fā)送關閉,此時需要判斷是否調用closeOutbound
        if(mSSLEngine.isOutboundDone()){
         return;
        }
        mSSLEngine.closeOutbound();
       }
       break;
      case NEED_TASK:
       // 執(zhí)行異步任務,我這里是同步執(zhí)行的,可以弄一個異步線程池進行。
       Runnable task = mSSLEngine.getDelegatedTask();
       if(task != null){
        task.run();
        // executor.execute(task); 這樣使用異步也是可以的,
        //但是異步就需要對ProcessHandShake的調用做特殊處理,因為異步的,像下面這直接是會導致瘋狂調用。
       }
       this.ProcessHandShake(null);  // 繼續(xù)處理握手
       break;
      case FINISHED:
       // 握手完成
       mHandshakeCompleted = true;
       this.OnHandCompleted();
       return;
      case NOT_HANDSHAKING:
       // 不需要握手
       if(!mHandshakeCompleted)
       {
        mHandshakeCompleted = true;
        this.OnHandCompleted();
       }
       return;
     }
    }

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

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

    這里需要額外說明一下,在握手期間也是會需要讀取數據的,因為服務器發(fā)送過來的數據需要我們執(zhí)行讀取并解密操作。而這個操作在一些其他的例子中直接使用了阻塞的讀取方式,我這里則是放在OnRead事件調用后進行處理,這樣才符合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ù)調用握手處理
     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))
          // 這里不需要對`pNetDataBuf`進行處理,因為ProcessSSLStatus里面已經做好處理了。
       return;
      if(res.getStatus() == Status.CLOSED)
       break;
      // 未完成握手時,需要繼續(xù)調用握手處理
      if(!mHandshakeCompleted)
       this.ProcessHandShake(res);
     }while(pNetDataBuf.hasRemaining());
     // 數據都解密完了,這個就可以清空了。
     if(!pNetDataBuf.hasRemaining())
       pNetDataBuf.clear();
    }

    感謝各位的閱讀,以上就是“Java怎么實現HTTPS訪問的操作”的內容了,經過本文的學習后,相信大家對Java怎么實現HTTPS訪問的操作這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

    向AI問一下細節(jié)

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

    AI