【自研网关系列】数字签名和url动态加密

🌈Yu-Gateway:基于 Netty 构建的自研 API 网关,采用 Java 原生实现,整合 Nacos 作为注册配置中心。其设计目标是为微服务架构提供高性能、可扩展的统一入口和基础设施,承载请求路由、安全控制、流量治理等核心网关职能。

🌈项目代码地址:https://github.com/YYYUUU42/YuGateway-master

如果该项目对你有帮助,可以在 github 上点个 ⭐ 喔 🥰🥰

🌈自研网关系列:可以点开专栏,参看完整的文档

目录

1、加密算法

1.1、对称加密

1.2、非对称加密

1.3、混合加密

1.4、摘要算法 + 数字签名

1.5、RSA 和 AES

2、网关实战

2.1、公钥获取

2.2、用公钥进行加密

2.3、对 url 进行加密

2.4、url 动态加密请求实现


1、加密算法

在网络传递数据的时候,为了防止数据被篡改,我们会对数据进行加密,数据加密分为对此加密和非对称加密。其中 RSA 和 AES,TLS 等加密算法是比较常用的。

这里就参考 https 解决 http 的明文风险,来完成这块的加密内容

1.1、对称加密

对称加密是指加密和解密使用相同的密钥的加密方法,基本流程包括以下步骤

  • 密钥生成
    • 双方协商生成一个共享密钥,或由一方生成密钥并安全地传输到另一方
  • 加密
    • 使用共享密钥对原始数据进行加密,得到加密后的数据
  • 传输
    • 将加密后的数据传输给另一方
  • 加密
    • 接收方使用相同的共享密钥对加密数据进行解密,得到原始数据

1.2、非对称加密

非对称加密是指加密和解密使用不同的密钥的加密方法,通常称为公钥和密钥

  • 密钥对生成
    • 生产一对秘钥,一个是公钥,另一个私钥。公钥可以公开,而私钥需要保密
  • 公钥分发
    • 将公钥发送给需要加密数据的一方
  • 加密
    • 使用公钥对原始数据进行加密,得到加密后的数据
  • 传输
    • 将加密后数据传输给另一方
  • 解密
    • 接受方使用私钥对数据进行解密,得到原始数据

1.3、混合加密

HTTPS 采用的是对称加密和非对称加密结合的「混合加密」方式:

  • 在通信建立前采用非对称加密的方式交换「会话秘钥」,后续就不再使用非对称加密。
  • 在通信过程中全部使用对称加密的「会话秘钥」的方式加密明文数据。

采用「混合加密」的方式的原因:

  • 对称加密只使用一个密钥,运算速度快,密钥必须保密,无法做到安全的密钥交换。
  • 非对称加密使用两个密钥:公钥和私钥,公钥可以任意分发而私钥保密,解决了密钥交换问题但速度慢。

1.4、摘要算法 + 数字签名

为了保证传输的内容不被篡改,我们需要对内容计算出一个「指纹」,然后同内容一起传输给对方。

对方收到后,先是对内容也计算出一个「指纹」,然后跟发送方发送的「指纹」做一个比较,如果「指纹」相同,说明内容没有被篡改,否则就可以判断出内容被篡改了。

那么,在计算机里会用摘要算法(哈希函数)来计算出内容的哈希值,也就是内容的「指纹」,这个哈希值是唯一的,且无法通过哈希值推导出内容。

通过哈希算法可以确保内容不会被篡改,但是并不能保证「内容 + 哈希值」不会被中间人替换,因为这里缺少对客户端收到的消息是否来源于服务端的证明。

那为了避免这种情况,计算机里会用非对称加密算法来解决,共有两个密钥:

  • 一个是公钥,这个是可以公开给所有人
  • 一个是私钥,这个必须由本人管理,不可泄露

这两个密钥可以双向加解密的,比如可以用公钥加密内容,然后用私钥解密,也可以用私钥加密内容,公钥解密内容。

流程的不同,意味着目的也不相同:

  • 公钥加密,私钥解密:这个目的是为了保证内容传输的安全,因为被公钥加密的内容,其他人是无法解密的,只有持有私钥的人,才能解密出实际的内容
  • 私钥加密,公钥解密:个目的是为了保证消息不会被冒充,因为私钥是不可泄露的,如果公钥能正常解密出私钥加密的内容,就能证明这个消息是来源于持有私钥身份的人发送的

一般我们不会用非对称加密来加密实际的传输内容,因为非对称加密的计算比较耗费性能的。

1.5、RSA 和 AES

加密的基本思想是将数据转换成一种掩盖了原始含义的形式,只有经过适当授权的人才能解密。使用相同或不同的密钥对数据进行加解密,如果加密和解密使用相同的密钥,则称该过程是对称的。如果使用不同的密钥,则该过程被定义为非对称的。

当今使用最广泛的两种加密算法是 AES 和 RSA。两者都非常有效和安全,但它们通常以不同的方式使用。

AES

AES 算法依次对每个 128 位数据块应用一系列数学变换。由于这种方法的计算要求较低,AES 可用于笔记本电脑和智能手机等消费类设备上进行数据加密,以及快速加密大量数据。

AES 是一种对称算法,它使用相同的 128、192 或 256 位密钥进行加密和解密。128、192 或 256 位的密钥可以理解为分别对应16、24和32个字节的16进制字符串密钥,AES 系统的安全性会随密钥长度呈指数增长。

即使使用 128 位密钥,通过对 2128 个可能的密钥值进行暴力枚举,来尝试破解 AES加密后的数据的任务也是个非常计算密集型的任务。事实上,AES 从未被破解,并且根据当前的技术趋势,预计在未来几年内仍将保持安全。

RSA

RSA 以麻省理工学院的科学家(Rivest、Shamir 和 Adleman)的名字命名, 于1977 年首次公布。它是一种非对称算法,它使用公开的已知密钥进行加密,但需要另外一个不同的密钥进行解密,这个不同的密钥只有预期的接收者知道。

AES 和 RSA 混合使用

AES 算法的一个主要问题是,作为一种对称算法,它要求加密方和解密方使用相同的密钥。这就产生了一个关键的密钥管理问题——如何将非常重要的密钥分发给分布在世界各地的授权接收者,而不会冒在传输途中某个地方考虑不周导致密钥泄露的巨大风险?答案是结合 AES 和 RSA 加密的优势。

在包括互联网在内的许多现代通信环境中,大量交换的数据都通过快速 AES 算法进行加密。为了获得解密数据所需的密钥,授权接收者发布一个公钥,同时保留一个只有他们知道的相关私钥。然后,发送方使用该公钥对他们自己的 AES 密钥进行RSA加密传输给接收方,接收方使用私钥解密得到AES密钥,再用该密钥对数据进行解密。

2、网关实战

根据上述知识,就可以通过RSA 和 AES 混合使用来实现 url 动态加密了,依然用 debug 的方式来讲解。由于没有前端,所以就用后端模拟前端加密,手动把加密后信息通过 postman 发送给后端。由于代码较多,就不展示全部代码了

2.1、公钥获取

这里主要目的是为每个用户生成一个 RSA 密钥对,并将私钥安全地存储在服务器端(这里是 Redis),然后将公钥返回给客户端。这样,客户端可以使用公钥进行加密操作,而只有拥有私钥的服务器才能解密,这是一种常见的公钥加密应用场景。

  1. 生成 RSA 密钥对:调用了 RSAUtil 类的 getKeyPair 方法,生成了一个 RSA 密钥对,包括公钥和私钥。
  2. 从请求的 Cookie 中获取 token:从 HTTP 请求的 Cookie 中获取 token。
  3. 从 token 中获取 userId:使用 JWTUtil 的 getClaimByToken 方法从 token 中获取用户的 ID。
  4. 将 RSA 私钥存储到 Redis 中:首先,创建了一个 JedisUtil 对象用于操作 Redis。然后,构造了一个特定格式的 key,该 key 包含了用户的 ID 和一个前缀security:key。接着,将 RSA 私钥和一个过期时间一起存储到 Redis 中。这里使用了 Redis 的 ZSet 数据结构,因为 ZSet 可以为每个元素设置一个分数,这里用来表示过期时间。
  5. 返回 RSA 公钥:最后,返回了生成的 RSA 公钥。

在许多安全应用中,每个用户都需要一对独特的公钥和私钥。这是因为:

  1. 私钥是保密的,只有用户自己知道。如果其他人知道了用户的私钥,他们就可以冒充用户进行操作,这会导致安全问题。
  2. 公钥是公开的,任何人都可以获取。公钥用于加密信息,只有对应的私钥才能解密这些信息。这样,即使别人获取了加密后的信息,也无法解密,因为他们没有私钥。
  3. 如果所有用户都使用同一对公钥和私钥,那么一旦这对密钥被泄露,所有用户的信息都会被暴露。而如果每个用户都有自己的公钥和私钥,即使某个用户的密钥被泄露,也只会影响到该用户,不会影响到其他用户。
/*** 给前端发送 RSA 公钥*/
@ApiInvoker(path = "/http-server/public-key")
@GetMapping("/http-server/public-key")
public String getRSASecretKey(HttpServletRequest request) {//获得秘钥RSAUtil.KeyPairInfo keyPair = RSAUtil.getKeyPair();//通过 token 得到 userIdCookie token = Stream.of(request.getCookies()).filter(cookie -> cookie.getName().equals(FilterConst.COOKIE_KEY)).findFirst().orElse(null);if (ObjectUtils.isEmpty(token)) {throw new RuntimeException("token is empty");}String userId = String.valueOf(Objects.requireNonNull(JWTUtil.getClaimByToken(token.getValue(), FilterConst.TOKEN_SECRET)).get(FilterConst.TOKEN_USERID_KEY));if (StringUtils.isEmpty(userId)) {throw new RuntimeException("parse token failed");}//将 RSA 私钥放进 redis(ZSet) 中// 这里使用 ZSet 数据结构来记录后端为不同用户生成的 RSA私钥,因为不同用户创建私钥的时间不同导致过期时间不同,hash无法为内部元素设置过期时间JedisUtil jedis = new JedisUtil();//security:key:{userId}    rsa:key:{rsa-privateKey}     {rsa-expireTime}String securityKey = FilterConst.SECURITY_KEY_PREFIX + ":" + userId;String rsaPrivateKey = FilterConst.RSA_PRIVATE_KEY_PREFIX + ":" + keyPair.getPrivateKey();long rsaExpireTime = System.currentTimeMillis() + FilterConst.RSA_PRIVATE_KEY_EXPIRE_TIME;if (!jedis.addScoreSet(securityKey, rsaPrivateKey, rsaExpireTime)) {throw new RuntimeException("save rsa-private-key into redis failed");}log.info("save rsa-privateKey into redis success, key: {}, expire: {}", FilterConst.RSA_PRIVATE_KEY_PREFIX, FilterConst.RSA_PRIVATE_KEY_EXPIRE_TIME);jedis.setExpire(FilterConst.RSA_PRIVATE_KEY_PREFIX, FilterConst.RSA_PRIVATE_KEY_EXPIRE_TIME);log.info("create rsa-publicKey: {}, rsa-privateKey: {}", keyPair.getPublicKey(), keyPair.getPrivateKey());return keyPair.getPublicKey();
}

这里返回的就是 RSA 公钥

2.2、用公钥进行加密

这里主要功能是使用 RSA 公钥对一个对称密钥进行加密,String symmetricKey = "yu123456" 就是我们的公钥 ,用公钥加密技术来安全地传输对称密钥,然后使用对称密钥进行数据加密。

/*** 模拟前端对自己得到的 RSA 公钥进行加密*/
@ApiInvoker(path = "/http-server/encrypt-symmetric-key")
@GetMapping("/http-server/encrypt-symmetric-key")
public String getEncryptSymmetricKey(HttpServletRequest request) throws Exception {String publicKeyPEM = request.getHeader("RSA-secret-key");String symmetricKey = "yu123456";byte[] decoded = Base64.getDecoder().decode(publicKeyPEM);KeyFactory keyFactory = KeyFactory.getInstance("RSA");X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decoded);PublicKey publicKey = keyFactory.generatePublic(keySpec);byte[] publicKeyBytes = publicKey.getEncoded();String publicKeyBase64 = Base64.getEncoder().encodeToString(publicKeyBytes);Cipher cipher = Cipher.getInstance("RSA");cipher.init(Cipher.ENCRYPT_MODE, publicKey);return RSAUtil.encryptPublicKey(symmetricKey, publicKeyBase64);
}

将刚刚的 RSA 公钥放进请求头RSA-secret-key中,再发送请求,得到含有对称密钥的 RSA 公钥

2.3、对 url 进行加密

  1. 首先就是定义一个对称密钥,就是前面通过RSA加密传递的对称密钥
  2. 然后就是需要传递的信息
  3. 用需要传递的信息进行 AES 加密,得到一个数字签名
  4. 需要传递的信息加上数字签名,形成最终的 url 加密后信息
/*** 模拟前端对 url 进行加密测试*/
@ApiInvoker(path = "/http-server/encrypt-url")
@GetMapping("/http-server/encrypt-url")
public String getEncryptURL(HttpServletRequest request) throws Exception {String symmetricPublicKey = "symmetric:key:yu123456";StringBuilder builder = new StringBuilder();String plainText = "userId=1";builder.append(plainText);builder.append("&signature=");builder.append(CryptoHelper.encryptParams(plainText, symmetricPublicKey));return CryptoHelper.encryptParams(builder.toString(), symmetricPublicKey);
}

2.4、url 动态加密请求实现

将上述加密后信息添加到请求路径中就变成

http://127.0.0.1:8888/http-server/v1/userId/encrypt/VWWXTp7npNdELumZ8Fm+V8O1U4NUakpu4zgom/9CcYZKsiW84TN4nQWTGMBngX1O

添加一些请求头

从请求头得到包含对称密钥的 RSA 秘钥,这个秘钥会和存在 Redis 中的私钥进行匹配,解密得到 AES 对称密钥

这里还会根据 Redis 中的私钥进行判断,看是否超过过期时间。

虽然使是用 set 存储,但在 Redis 中用 zset 存储的,所以得到的还是最新的私钥

解密完 RSA 秘钥得到 ASE 对称密钥后,会把这个秘钥存储到 Redis 中

然后就到了处理加密的 URL和验证 URL 的完整性部分了

首先还是会有白名单部分的

然后就根据 Cookie 中的 token 得到 userId,再通过 userId 得到刚刚存储到 Redis 中的 AES 密钥,通过 AES 密钥对加密 url 进行解密

更新前

解密后更新

当然也会通过数字签名验证信息是否有被修改

public static boolean verifySignature(MultiValueMap<String, String> queryParams, String signature, String symmetricKey) {StringBuilder builder = new StringBuilder();for (Map.Entry<String, List<String>> entry : queryParams.entrySet()) {if (!"signature".equals(entry.getKey())) {builder.append(entry.getKey()).append("=").append(String.join(",", entry.getValue())).append("&");}}builder.setLength(builder.length() - 1);String computedSignature = encryptParams(builder.toString(), symmetricKey).replace("+", " ");return computedSignature.equals(signature);
}

最终得到原始路径

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/9523.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Java EE】数据库连接池详解

文章目录 &#x1f38d;数据库连接池&#x1f338;Hikari&#x1f338;Druid &#x1f340;MySQL开发企业规范⭕总结 &#x1f38d;数据库连接池 在上⾯Mybatis的讲解中,我们使⽤了数据库连接池技术,避免频繁的创建连接,销毁连接 下⾯我们来了解下数据库连接池 数据库连接池负…

「ETL实战」搭建数仓,解决多源业务系统关联分析难题(定制化业务)

在大数据分析盛行的今天&#xff0c;关联分析作为数据挖掘和业务洞察的重要手段&#xff0c;受到了极大关注。然而&#xff0c;随着数据量的激增和源业务系统的复杂性增加&#xff0c;关联分析的性能问题逐渐成为了一个不可忽视的挑战。 本文将介绍借助ETL工具&#xff0c;如何…

大数据面试题 —— 数据库

目录 关系型数据库与非关系型数据库的区别数据库三范式MySQL中 drop、delete、truncate的区别MySQL中 char和 varchar 的区别MySQL中inner join、left join、right join以及full join的区别MySQL中 having 和 where 的区别count(*)、count(1)、count(列名)的区别MySQL中视图和表…

Threejs 学习笔记 | 灯光与阴影

文章目录 Threejs 学习笔记 | 灯光与阴影如何让灯光照射在物体上有阴影LightShadow - 阴影类的基类平行光的shadow计算投影属性 - DirectionalLightShadow类平行光的投射相机 聚光灯的shadow计算投影属性- SpotLightShadow类聚光灯的投射相机 平行光 DirectionalLight聚光灯 Sp…

12.轻量级锁原理及其实战

文章目录 轻量级锁原理及其实战1.轻量级锁的核心原理2.轻量级锁的演示2.1.轻量级锁的演示代码2.2.结果分析 3.轻量级锁的分类3.1.普通自旋锁3.2.自适应自旋锁 4.轻量级锁的膨胀 轻量级锁原理及其实战 引入轻量级锁的主要目的是在多线程环境竞争不激烈的情况下&#xff0c; 通过…

verilog中不重叠序列检测

编写一个序列检测模块&#xff0c;检测输入信号&#xff08;a&#xff09;是否满足011100序列&#xff0c; 要求以每六个输入为一组&#xff0c;不检测重复序列&#xff0c;例如第一位数据不符合&#xff0c;则不考虑后五位。一直到第七位数据即下一组信号的第一位开始检测。当…

C++下使用Matplotlib-cpp的一些配置

Aconda3安装使用 https://mirrors.tuna.tsinghua.edu.cn/help/anaconda/下载 切换清华源 conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/ con…

修改latex中block中公式与block标题间隔过大的问题

修改block中公式与block间隔过大的问题 如图的block中公式出现了空白:代码见下方 \begin{proof}[证明]\begin{align*}&Z\alpha \beta _XX\beta _YY\varepsilon \rightarrow XZ\alpha X\beta _XX^2\beta _YXY\varepsilon X&\\&E\left( Z \right) \alpha \beta _XE\…

STM32实现1.8寸液晶屏 LCD SPI串口显示屏模块 TFT彩屏(标准库和HAL库实现)

目录 一、所选模块 液晶模块选择&#xff08;淘宝上均有售卖&#xff09; 模块引脚 二、嵌入式单片机型号 三、接线表设计 四、开发环境版本说明 五、标准库实现 六、HAL库实现 七、完整工程&#xff08;内含标准库和HAL库源码&#xff09; 代码链接 一、所选模块 液…

pycharm如何对for循环中第n次循序执行断点

目录 在 PyCharm 中&#xff0c;您可以设置条件断点来实现这个功能&#xff0c;这样只有在满足特定条件时断点才会被触发。以下是设置仅在 for 循环的第 n 次迭代时触发断点的步骤&#xff1a; 设置断点&#xff1a; 首先&#xff0c;找到您想要在 for 循环中设置断点的行。点击…

《应用现代化技术能力成熟度评估模型》介绍

在中国软件行业协会、应用现代化产业联盟以及中国电子技术标准化研究院的指导下&#xff0c;产业多家企业共同支持和参与下&#xff0c;完成的《应用现代化技术能力成熟度评估模型》标准。该标准从应用敏捷、稳定可靠、安全可信、业务智能、成本优化五大维度及22个能力项来评估…

计算机网络学习记录 网络的大概认识 Day1

你好,我是Qiuner. 为记录自己编程学习过程和帮助别人少走弯路而写博客 这是我的 github gitee 如果本篇文章帮到了你 不妨点个赞吧~ 我会很高兴的 &#x1f604; (^ ~ ^) 想看更多 那就点个关注吧 我会尽力带来有趣的内容 计算机网络学习记录Day1 本文基于1.1 计算机网络在信息…

Highcharts 实现3D饼图 tooltip轮播

实现3D饼图&#xff0c;并且轮播显示tooltip 自定义toottip样式 import Highcharts from highcharts; import highcharts from highcharts; import highcharts3d from highcharts/highcharts-3d;highcharts3d(Highcharts); highcharts3d(highcharts); import { useEffect, use…

2024 VMware VCP一条龙通关-送题库

VMware VCP-DCV 2024&#xff08;2V0-21.23&#xff09;认证考试&#xff0c;2024年可高分通过。 1.5. An administrator has a host profile named Standard-Config. The administrator wants to change the other host profiles to use only the storage configuration setti…

自定义付费报名表单系统源码 带完整的安装代码包以及安装代码包搭建部署教程

在当今数字化时代&#xff0c;各种在线服务需求日益增长&#xff0c;其中&#xff0c;自定义付费报名表单系统成为了许多机构、组织和企业不可或缺的一部分。为了满足这一市场需求&#xff0c;小编给大家分享一款功能强大、易于部署的自定义付费报名表单系统源码。本文将为您详…

Python嵌套绘图并为条形图添加自定义标注

论文绘图时经常需要多图嵌套&#xff0c;正好最近绘图用到了&#xff0c;记录一下使用Python实现多图嵌套的过程。 首先&#xff0c;实现 Seaborn 分别绘制折线图和柱状图。 绘制折线图import seaborn as snsimport matplotlib.pyplot as pltimport warningswarnings.filterw…

STM32CubeMX学习笔记30---FreeRTOS内存管理

一、简介 1 基本概念 FreeRTOS 操作系统将内核与内存管理分开实现&#xff0c;操作系统内核仅规定了必要的内存管理函数原型&#xff0c;而不关心这些内存管理函数是如何实现的&#xff0c;所以在 FreeRTOS 中提供了多种内存分配算法&#xff08;分配策略&#xff09;&#xf…

《二十二》Qt 音频编程实战---做一个音频播放器

1.UI界面制作 作为一个音乐播放器&#xff0c;最基础的肯定就是播放、暂停、上一首以及下一首&#xff0c;为了使这个界面好看一点&#xff0c;还加入了音量控制、进度条、歌曲列表等内容&#xff0c;至于这种配色和效果好不好看&#xff0c;我也不知道&#xff0c;个人审美一如…

智启未来:富唯智能AI-ICDP引领的可重构柔性装配产线

在全球制造业竞争日益激烈的今天&#xff0c;如何快速响应市场变化、提高生产效率、降低生产成本&#xff0c;成为了企业面临的重要挑战。随着产品个性化时代的到来&#xff0c;装配产品频繁变换&#xff0c;多品种小批量的生产模式逐渐成为主流。在这一背景下&#xff0c;富唯…

python 前台页面和oracle数据库联动案例-用户注册

今天是python 入门day3&#xff0c;案例实现界面如图&#xff0c;比较简单&#xff0c; 一个简单的用户注册页面&#xff0c;并且可以与Oracle数据库进行交互。 界面如图&#xff1a; 实现这个简单demo的过程中&#xff0c;遇到很多错误&#xff0c; 1、提交过程中提示主键不…