用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.mzph.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…