用Java NIO模擬HTTPS

HTTPS流程?

名詞解釋:
?? ?R1:隨機數1 R2:隨機數2 R3:隨機數3 publicKey:公鑰 privateKey:私鑰

要提供https服務,服務端需要安裝數字證書,在(TCP建立連接之后)TLS握手時發給客戶端,客戶端驗證證書,證書包含公鑰。
step1?
客戶端 client hello + R1 ?
服務端 server hello + R2 + publicKey
step2
客戶端 R3 publicKey加密 預主密鑰
服務端 privateKey解密得到R3
step3
客戶端與服務端使用相同的對稱密鑰算法生成會話密鑰
客戶端 R3 + R1 + R2 -> 生成會話密鑰? 主密鑰
服務端 R3 + R1 + R2 -> 生成會話密鑰
step4
正式通信 對稱密鑰(會話密鑰)加密數據

HttpsServer


import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;public class HttpsServer {private static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);private static Map<String, Object> keyMap;private static final byte[] randomList = new byte[3];private static volatile Map<SocketChannel, Boolean> channelBooleanMap = new ConcurrentHashMap<>();public static void main(String[] args) throws Exception {keyMap = init();scheduler.scheduleAtFixedRate(() -> {channelBooleanMap.forEach((k, v) -> {if (!v) {channelBooleanMap.remove(k);}});}, 0, 1, TimeUnit.SECONDS);startServer();}public static Map<String, Object> init() throws NoSuchAlgorithmException {Map<String, Object> map = EncryptUtil.initKey();return Collections.unmodifiableMap(map);}public static void startServer() throws Exception {ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();serverSocketChannel.configureBlocking(false);serverSocketChannel.bind(new InetSocketAddress(8080));Selector selector = Selector.open();serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);System.out.println("服務器監聽....");while (true) {int select = selector.select();if (select > 0) {Set<SelectionKey> selectionKeys = selector.selectedKeys();Iterator<SelectionKey> iterator = selectionKeys.iterator();while (iterator.hasNext()) {SelectionKey key = iterator.next();iterator.remove();if (key.isValid()) {if (key.isAcceptable()) {ServerSocketChannel channel = (ServerSocketChannel) key.channel();SocketChannel socketChannel = channel.accept();System.out.println("連接:" + socketChannel.getRemoteAddress());socketChannel.configureBlocking(false);channelBooleanMap.put(socketChannel, true);// 為每個連接到的channel分配一個緩沖區,channel間互相隔離ByteBuffer buffer = ByteBuffer.allocate(8 * 1024);socketChannel.register(selector, SelectionKey.OP_READ, buffer);}if (key.isReadable()) {SocketChannel channel = (SocketChannel) key.channel();try {ByteBuffer buffer = ByteBuffer.allocate(1024);byte b;int read, count = 0;while (true) {if ((read = channel.read(buffer)) > 0) {count += read;System.out.println("count:" + count);}if (count == 14) {buffer.flip();break;}if (count == 65) {buffer.flip();break;}if (count == 32) { // 正式通信buffer.flip();break;}}b = buffer.get(0);if (b == 13) { // 第一次同步消息byte[] array = buffer.array();System.out.println(new String(array, 1, 12));byte r1 = array[13]; // 隨機數1 客戶端發給服務端System.out.println("隨機數1:" + r1); // 大小端randomList[0] = r1;} else if (b == 64) { // 第二次同步消息byte[] array = buffer.array();byte[] data = new byte[b];System.arraycopy(array, 1, data, 0, b);byte[] bytes = EncryptUtil.decryptByPrivateKey(data, EncryptUtil.getPrivateKey(keyMap));System.out.println("隨機數3=" + bytes[0]);randomList[2] = bytes[0];System.out.println("randomList:" + Arrays.toString(randomList));// 生成會話密鑰byte[] sessionKey = EncryptUtil.hmacSHA256(EncryptUtil.HmacSHA256_key.getBytes(), randomList);SetCache.add(sessionKey);System.out.println("會話密鑰:" + Arrays.toString(sessionKey));} else { // 正式通信byte[] array = new byte[32];buffer.get(array);System.out.println("array=" + Arrays.toString(array));if (Arrays.compare(array, SetCache.get()) == 0) {System.out.println("會話密鑰驗證成功");} else {System.out.println("會話密鑰驗證失敗");}}} catch (Exception e) {channelBooleanMap.put(channel, false);key.cancel();channel.close();System.out.println("有連接關閉...");break;}System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");// 注冊事件:可能會觸發多余的寫事件channel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
//                            if (flag) { // 通信結束標識
//                                channel.register(selector, SelectionKey.OP_READ);
//                            } else {
//                                channel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
//                            }}if (key.isWritable()) {System.out.println("觸發寫事件....");SocketChannel channel = (SocketChannel) key.channel();ByteBuffer buffer = ByteBuffer.allocate(1024);String serverHello = "Server Hello";buffer.put(serverHello.getBytes());byte b = (byte) new Random().nextInt(Byte.MAX_VALUE);randomList[1] = b; // 隨機數2 服務端發送給客戶端buffer.put(b);// 發送公鑰給客戶端byte[] publicKey = EncryptUtil.getPublicKey(keyMap);byte len = (byte) publicKey.length;System.out.println("publicKey.length:" + len);buffer.put(len);buffer.put(publicKey);// 注意:往channel中寫緩沖區前,必須切換到讀模式,否則無法觸發讀事件buffer.flip();if (!channelBooleanMap.get(channel)) {System.out.println("通道已關閉...");channel.register(selector, key.interestOps() & ~SelectionKey.OP_WRITE);break;}channel.write(buffer);//                            channel.socket().getOutputStream().flush();//                            channel.write(ByteBuffer.wrap(serverHello.getBytes()));
//                            channel.write(ByteBuffer.wrap(new byte[]{b}));System.out.println(Arrays.toString(buffer.array()));System.out.println("隨機數2:" + b);channel.register(selector, key.interestOps() & ~SelectionKey.OP_WRITE);}}}}}}
}

HttpsClient


import java.io.IOException;
import java.io.InputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;public class HttpsClient {private static final List<byte[]> key = new ArrayList<>();private static final byte[] randomList = new byte[3];private static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);public static void main(String[] args) throws Exception {Socket socket = new Socket("localhost", 8080);socket.setSoLinger(true, 0); // 設置關閉連接時的等待時間
//        socket.setReuseAddress(true); // 設置端口重用String clientHello = "Client Hello";int anInt = new Random().nextInt(Byte.MAX_VALUE);System.out.println("Client 隨機數1:" + anInt);randomList[0] = (byte) anInt;socket.getOutputStream().write(new byte[]{13});socket.getOutputStream().write(clientHello.getBytes());socket.getOutputStream().write(anInt);InputStream inputStream = socket.getInputStream();byte[] buffer = new byte[12];int read, count = 0;while (count < 12) {read = inputStream.read(buffer);count += read;}String cmd = new String(buffer);System.out.println("Server " + cmd);// 讀取第二個隨機數int read1 = inputStream.read();System.out.println("Server 隨機數2:" + read1);randomList[1] = (byte) read1;// 讀取公鑰int len = inputStream.read();System.out.println("publicKey len: " + len);byte[] publicKey = new byte[len];int count2 = 0;while (count2 < len) {int read2 = inputStream.read(publicKey);count2 += read2;}key.add(publicKey);System.out.println("輸入任何字符開始第二次通信...");System.in.read();// 客戶端生成第三個隨機數int r3 = new Random().nextInt(Byte.MAX_VALUE);byte[] bytes = {(byte) r3};randomList[2] = bytes[0];System.out.println("隨機數3=" + Arrays.toString(bytes));byte[] data = EncryptUtil.encryptByPublicKey(bytes, publicKey);socket.getOutputStream().write(data.length); // 64socket.getOutputStream().write(data);System.out.println("randomList:" + Arrays.toString(randomList));// 生成會話密鑰byte[] sessionKey = EncryptUtil.hmacSHA256(EncryptUtil.HmacSHA256_key.getBytes(), randomList);SetCache.add(sessionKey);System.out.println("會話密鑰:" + Arrays.toString(sessionKey));System.out.println("密鑰長度:" + SetCache.get().length);System.out.println("開始正式通信...");System.out.println("發送密鑰....");socket.getOutputStream().write(SetCache.get());System.out.println("end...");socket.close();}public void test() throws IOException {SocketChannel channel = SocketChannel.open();channel.configureBlocking(false);Selector selector = Selector.open();channel.register(selector, SelectionKey.OP_CONNECT);channel.connect(new InetSocketAddress("localhost", 8080));while (true) {int select = selector.select();if (select > 0) {Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();while (iterator.hasNext()) {SelectionKey key = iterator.next();iterator.remove();if (key.isConnectable()) {if (channel.finishConnect()) {System.out.println("客戶端連接成功...");channel.register(selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ);}}if (key.isWritable()) {System.out.println("Client send...");SocketChannel channel1 = (SocketChannel) key.channel();ByteBuffer buffer = ByteBuffer.allocate(16);String ClientHello = "Client Hello";int r1 = new Random().nextInt(100);buffer.put(ClientHello.getBytes());buffer.putInt(r1);channel1.write(buffer);channel1.register(selector, key.interestOps() & ~SelectionKey.OP_WRITE);System.out.println("Client send end...");}if (key.isReadable()) {System.out.println("Client receive...");SocketChannel channel1 = (SocketChannel) key.channel();ByteBuffer buffer = ByteBuffer.allocate(16);channel1.read(buffer);byte[] array = buffer.array();System.out.println(new String(array, 0, 12));System.out.println(new String(array, 12, 16));channel1.register(selector, SelectionKey.OP_WRITE);}}}}}
}

EncryptUtil

import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;public final class EncryptUtil {// 非對稱加密算法private static final String KEY_ALGORITHM = "RSA";// 公鑰private static final String PUBLIC_KEY = "PUBLIC_KEY";// 私鑰private static final String PRIVATE_KEY = "PRIVATE_KEY";// RSA密鑰長度 默認1024 必須為64的倍數private static final int KEY_SIZE = 512;public static final String HmacSHA256_key = "HmacSHA256_key";public static Map<String, Object> initKey() throws NoSuchAlgorithmException {// 實例化密鑰對生成器KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);keyPairGenerator.initialize(KEY_SIZE);KeyPair keyPair = keyPairGenerator.generateKeyPair();RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();Map<String, Object> keyMap = new HashMap<>();keyMap.put(PUBLIC_KEY, publicKey);keyMap.put(PRIVATE_KEY, privateKey);return keyMap;}public static byte[] getPublicKey(Map<String, Object> keyMap) throws Exception {Key key = (Key) keyMap.get(PUBLIC_KEY);return key.getEncoded();}public static byte[] getPrivateKey(Map<String, Object> keyMap) throws NoSuchAlgorithmException {Key key = (Key) keyMap.get(PRIVATE_KEY);return key.getEncoded();}public static byte[] encryptByPublicKey(byte[] data, byte[] key) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {// 取得公鑰X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(key);KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);// 對數據加密Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());cipher.init(Cipher.ENCRYPT_MODE, publicKey);return cipher.doFinal(data);}public static byte[] decryptByPrivateKey(byte[] data, byte[] key) throws Exception {// 取得私鑰PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(key);KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);// 生成私鑰PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);// 對數據解密Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());cipher.init(Cipher.DECRYPT_MODE, privateKey);return cipher.doFinal(data);}/*** HmacSHA256算法,返回的結果始終是32位** @param key     加密的鍵,可以是任何數據* @param content 待加密的內容* @return 加密后的內容* @throws Exception*/public static byte[] hmacSHA256(byte[] key, byte[] content) throws Exception {Mac hmacSha256 = Mac.getInstance("HmacSHA256");hmacSha256.init(new SecretKeySpec(key, 0, key.length, "HmacSHA256"));byte[] hmacSha256Bytes = hmacSha256.doFinal(content);return hmacSha256Bytes;}
}

SetCache

public class SetCache {private static final byte[] cache = new byte[32];public static void add(byte[] key) {System.arraycopy(key, 0, cache, 0, 32);}public static byte[] get() {return cache;}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/75942.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/75942.shtml
英文地址,請注明出處:http://en.pswp.cn/web/75942.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

樹莓派_利用Ubuntu搭建gitlab

樹莓派_利用Ubuntu搭建gitlab 一、給樹莓派3A搭建基本系統 1、下載系統鏡像 https://cdimage.ubuntu.com/ubuntu/releases/18.04/release/ 2、準備系統SD卡 二、給樹莓派設備聯網 1、串口后臺登錄 使用串口登錄后臺是最便捷的&#xff0c;因為前期網絡可能不好直接成功 默…

Hook_Unfinished

#include <windows.h>// 假設這兩個函數是存在的 void DoRD() {} void 改堆棧cal1() {} void 改回堆棧cal1() {}__declspec(naked) void HOOKcall() {__asm{pushadnop}__asm{popadmov eax, dword ptr [esi 8]sub eax, ecxretn} }int main() {// 第一個 Hook 操作DWORD H…

數據結構(六)——紅黑樹及模擬實現

目錄 前言 紅黑樹的概念及性質 紅黑樹的效率 紅黑樹的結構 紅黑樹的插入 變色不旋轉 單旋變色 雙旋變色 插入代碼如下所示&#xff1a; 紅黑樹的查找 紅黑樹的驗證 紅黑樹代碼如下所示&#xff1a; 小結 前言 在前面的文章我們介紹了AVL這一棵完全二叉搜索樹&…

c# 數據結構 鏈表篇 有關雙向鏈表的一切

本人能力有限,如有不足還請斧正 目錄 0.雙向鏈表的好處 1.雙向鏈表的分類 2.不帶頭節點的標準雙向鏈表 節點類:有頭有尾 鏈表類:也可以有頭有尾 也可以只有頭 增 頭插 尾插 刪 查 改 遍歷 全部代碼 3.循環雙向鏈表 節點類 鏈表類 增 頭插 尾插 刪 查 遍歷…

Numba 從零基礎到實戰:解鎖 Python 性能新境界

Numba 從零基礎到實戰&#xff1a;解鎖 Python 性能新境界 一、引言 在 Python 的世界里&#xff0c;性能一直是一個備受關注的話題。Python 以其簡潔易讀的語法和豐富的庫生態&#xff0c;深受開發者喜愛&#xff0c;但在處理一些計算密集型任務時&#xff0c;其執行速度往往…

單位門戶網站被攻擊后的安全防護策略

政府網站安全現狀與挑戰 近年來&#xff0c;隨著數字化進程的加速&#xff0c;政府門戶網站已成為政務公開和服務公眾的重要窗口。然而&#xff0c;網絡安全形勢卻日益嚴峻。國家互聯網應急中心的數據顯示&#xff0c;政府網站已成為黑客攻擊的重點目標&#xff0c;被篡改和被…

Spring Boot 項目三種打印日志的方法詳解。Logger,log,logger 解讀。

目錄 一. 打印日志的常見三種方法&#xff1f; 1.1 手動創建 Logger 對象&#xff08;基于SLF4J API&#xff09; 1.2 使用 Lombok 插件的 Slf4j 注解 1.3 使用 Spring 的 Log 接口&#xff08;使用頻率較低&#xff09; 二. 常見的 Logger&#xff0c;logger&#xff0c;…

NI的LABVIEW工具安裝及卸載步驟說明

一.介紹 最近接到個轉交的項目&#xff0c;項目主要作為上位機工具開發&#xff0c;在對接下位機時&#xff0c;有用到NI的labview工具。labview軟件是由美國國家儀器&#xff08;NI&#xff09;公司研制開發的一種程序開發環境&#xff0c;主要用于汽車測試、數據采集、芯片測…

cmd 終端輸出亂碼問題 |Visual Studio 控制臺輸出中文亂碼解決

在網上下載&#xff0c;或者移植別人的代碼到自己的電腦&#xff0c;使用VS運行后&#xff0c;控制臺輸出中文可能出現亂碼。這是因為源代碼的編碼格式和控制臺的編碼格式不一致。 文章目錄 查看源代碼文件編碼格式查看輸出控制臺編碼格式修改編碼格式修改終端代碼頁 補充總結 …

A009-基于pytest的網易云自動化測試

題 目 :基于pytest的網易云自動化測試 主要內容 綜合應用所學的軟件測試理論和方法,實現網易云的功能自動化測試。 (1)自動化測試介紹; (2)自動化功能測試框架介紹; (3)設計功能測試用例 (4)書寫自動化測試腳本; (5)測試評價與結論。 任務要求 (1)能…

LVGL Video控件和Radiobtn控件詳解

LVGL Video控件和Radiobtn控件詳解 一、 Video控件詳解1. 概述2. 創建和初始化3. 基本屬性設置4. 視頻控制5. 回調函數6. 高級功能7. 注意事項 二、Radiobtn控件詳解1. 概述2. 創建和初始化3. 屬性設置4. 狀態控制5. 組管理6. 事件處理7. 樣式設置8. 注意事項 三、效果展示四、…

AbortController:讓異步操作隨時說停就停

AbortController&#xff1a;讓異步操作隨時說停就停 一、什么是 AbortController&#xff1f; AbortController 是 JavaScript 在瀏覽器和部分 Node.js 環境中提供的全局類&#xff0c;用來中止正在進行或待完成的異步操作&#xff08;如 fetch() 請求、事件監聽、可寫流、數…

機器學習 從入門到精通 day_04

1. 決策樹-分類 1.1 概念 1. 決策節點 通過條件判斷而進行分支選擇的節點。如&#xff1a;將某個樣本中的屬性值(特征值)與決策節點上的值進行比較&#xff0c;從而判斷它的流向。 2. 葉子節點 沒有子節點的節點&#xff0c;表示最終的決策結果。 3. 決策樹的…

C++ Primer (第五版)-第十三章 拷貝控制

文章目錄 概述13.1拷貝、賦值與銷毀合成拷貝構造函數拷貝初始化參數和返回值拷貝初始化的限制編譯器可以繞過拷貝構造函數拷貝運算符析構函數三/五原則使用default阻止拷貝合成的拷貝控制成員可能是刪除的 private拷貝控制拷貝控制和資源管理行為像值的類類值拷貝賦值運算符定義…

Vue el-from的el-form-item v-for循環表單如何校驗rules(一)

實際業務需求場景&#xff1a; 新增或編輯頁面&#xff08;基礎信息表單&#xff0c;一個數據列表的表單&#xff09;&#xff0c;數據列表里面的表單數是動態添加的。數據可新增、可刪除&#xff0c;在表單保存前&#xff0c;常常需要做表單必填項的校驗&#xff0c;校驗通過以…

測試100問:http和https的區別是什么?

哈嘍&#xff0c;大家好&#xff0c;我是十二&#xff0c;今天給大家分享的問題是&#xff1a;http和https的區別是什么&#xff1f; 首先我們要知道 HTTP 協議傳播的數據都是未加密的&#xff0c;也就是明文的&#xff0c;因此呢使用 http協議傳輸一些隱私信息也就非常不安全&…

YOLOv3超詳細解讀(三):源碼解析:數據處理模塊

一、概述 YOLOv3&#xff08;You Only Look Once v3&#xff09;是一種高效的目標檢測算法&#xff0c;其數據處理模塊是訓練和推理流程的核心部分。本文將深入分析Ultralytics團隊基于PyTorch實現的YOLOv3源碼中的數據處理模塊&#xff0c;重點探討數據加載、預處理和數據增強…

每日算法(雙指針算法)(Day 1)

雙指針算法 1.算法題目&#xff08;移動零&#xff09;2.講解算法原理3.編寫代碼 1.算法題目&#xff08;移動零&#xff09; 2.講解算法原理 數組劃分&#xff0c;數組分塊&#xff08;快排里面最核心的一步&#xff09;只需把0改為tmp 雙指針算法&#xff1a;利用數組下標來…

2025藍橋杯python A組省賽 題解

真捐款去了&#xff0c;好長時間沒練了&#xff0c;感覺腦子和手都不轉悠了。 B F BF BF 賽時都寫假了&#xff0c; G G G 也只寫了爆搜。 題解其實隊友都寫好了&#xff0c;我就粘一下自己的代碼&#xff0c;稍微提點個人的理解水一篇題解 隊友題解 2025藍橋杯C A組省賽 題…

測試基礎筆記第四天(html)

提示&#xff1a;文章寫完后&#xff0c;目錄可以自動生成&#xff0c;如何生成可參考右邊的幫助文檔 文章目錄 html介紹1. 介紹2.骨架標簽3.常用標簽標題標簽段落標簽超鏈接標簽圖片標簽換行和空格標簽布局標簽input標簽&#xff08;變形金剛&#xff09;form標簽列表標簽 htm…