node 后端和浏览器前端,有关 RSA 非对称加密的完整实践, 前后端匹配的代码演示

前言

前天,写了一篇文章《我设计的一个安全的 web 系统用户密码管理流程》,里面提到了有关非对称加密 RSA 的一些事情。思想归思想,实践要重于理论,所以我想趁周末,来完成这个时间。

结果发现现实比理想要困难许多,这事儿折腾了我昨儿一天,今天,终于完成了。

主要困难点有如下:

  1. 之前我们的 RSA 一般使用 pkcs1 格式。但是在最新的 NODE v22 版本中,这种格式被认为是不安全的,所以要改。(或许之前的版本就更新了,这里我没去查升级日志,只确定 v22+ 肯定是不行)
  2. 之前我们前端代码中经常使用的 JSEncrypt 库已经好几年不更新了,对最新的规范支持不完整。
  3. 我一直使用的 1024 的秘钥,结果这里也出现了问题。

也就是说,我之前代码跑得好好的,经过这么一升级,代码烂了!!!

中间我踩过的坑太多了,字字泣血,这里只展示最终成果吧。

生成秘钥

# 生成私钥
openssl genrsa -out src/config/key/rsa_private_key.pem 2048
# 生成公钥
openssl rsa -in src/config/key/rsa_private_key.pem -pubout -out src/config/key/rsa_public_key.pem

NODE 后端

在后端时间库的选择上,我一开始选择的是 node-rsa 库,但是在不断的调整中,最后选择了 node-forge 库。

但,我并不确定 node-rsa 库是有问题的,因为最终我发现我的问题并不是后端的问题,而是我前端的问题。以下代码是可用的,如果我等下还有心情,我再尝试一下 node-rsa

import { readFile } from 'node:fs/promises'
import { RSA_KEY } from '@/config'
import * as forge from 'node-forge'const { RSA_PRIVATE_KEY_PATH, RSA_PUBLIC_KEY_PATH } = RSA_KEY// 公钥加密方法
export const encrypt = (str: string) => {return new Promise((resolve, reject) => {readFile(RSA_PUBLIC_KEY_PATH, 'utf-8').then((data) => {try {const publicK = forge.pki.publicKeyFromPem(data)const res = publicK.encrypt(str, 'RSA-OAEP', {md: forge.md.sha256.create(),mgf1: forge.md.sha256.create(),})const Base64Res = forge.util.encode64(res)resolve(Base64Res)} catch (e) {reject(new Error(`RSA加密失败: ${e.message || e}`))}})})
}// 私钥解密方法
export const decrypt = (str: string) => {return new Promise((resolve, reject) => {if (!str || typeof str !== 'string') {return reject(new Error('RSA解密失败: 无效的输入'))}readFile(RSA_PRIVATE_KEY_PATH, 'utf-8').then((data) => {try {const privateK = forge.pki.privateKeyFromPem(data)const base64Str = forge.util.decode64(str)const res = privateK.decrypt(base64Str, 'RSA-OAEP', {md: forge.md.sha256.create(),mgf1: forge.md.sha256.create(),})resolve(res)} catch (e) {reject(new Error(`RSA解密失败: ${e.message || e}`))}})})
}

浏览器前端

在JSEncrypt 确认不可用的前提下,我找了很多资料,遇到了很多坑。经过不断的研究,我决定,不使用任何库类,而是直接使用现代浏览器支持的 Web Crypto API 特性。
Web Crypto API 的支持情况
如上图所示,Web Crypto API 已经被广泛的现代浏览器所支持了。估计,这也是为什么 JSEncrypt 这个库不更新了的原因。

完整代码如下:

async function importPublicKey(pem: string) {const pemHeader = "-----BEGIN PUBLIC KEY-----"const pemFooter = "-----END PUBLIC KEY-----"const pemContents = pem.replace(pemHeader, "").replace(pemFooter, "").replace(/\s+/g, "")const binaryDer = base64ToArrayBuffer(pemContents)return await window.crypto.subtle.importKey("spki",binaryDer,{name: "RSA-OAEP",hash: { name: "SHA-256" }},true,["encrypt"])
}function base64ToArrayBuffer(base64: string) {const binaryString = atob(base64)const bytes = new Uint8Array(binaryString.length)for (let i = 0; i < binaryString.length; i++) {bytes[i] = binaryString.charCodeAt(i)}return bytes.buffer
}export async function encrypt(publicKeyStr: string, message: string) {const importedPublicKey = await importPublicKey(publicKeyStr)const encrypted = await crypto.subtle.encrypt({ name: "RSA-OAEP" },importedPublicKey,new TextEncoder().encode(message))// 将加密后的二进制数据转换为 base64 字符串return btoa(String.fromCharCode.apply(null, new Uint8Array(encrypted)))
}

主要关键点是,从接口拿到后端的公钥后,需要去头去尾,转换一下格式,才能被使用。

好的,以上代码都是在 node v22 环境下验证可用的,预计短期几年内应该不会有太大变化。

后记

其实,以上代码写出来还好,我遇到把我卡住的关键点是我服务端使用的是 1024 的秘钥。而使用这个秘钥,用来加密 sha256 的 hash 值的时候一直报错。我一直以为是我前后端代码没搞好的问题。但是,经过不断的调试猛然发现,其在加密短字符串的时候是没问题的,而 sha256 的 hash 值是 64 位的长度。

在我抓耳挠腮的时候,我福至心灵,把服务端的秘钥换成 2048 的秘钥后,所有问题就都解决了,顿时间,拨云见日。

最终,我查看资料,知道 使用 ‌SHA-256‌ 时,1024 位 RSA-OAEP 加密支持单次最大 ‌62 字节明文‌。这里,多出了2个字节。

好吧,我今天可以睡个好觉了!祝各位看官,安康!

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

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

相关文章

【自然语言处理与大模型】Windows安装RAGFlow并接入本地Ollama模型

本文给大家带来一个实用的RAG框架——RAGFlow。我们来看看它的自我介绍吧&#xff01; 还是老规矩&#xff0c;它是什么&#xff1f;有什么用&#xff1f;这些官方文档都可以简单的查到&#xff08;官方中文README_zh.md&#xff09;。下面我带大家一步步安装并实现一个知识库问…

MySQL 自启动时报错can‘t create PID file: No such file or directory

系统版本&#xff1a;Red Hat Enterprise Linux Server release 7.0 MySQL版本&#xff1a;5.7.16-1.el6.x86_64 安装MySQL后&#xff0c;设置好了开机自启动。 但是重启服务器后&#xff0c;MySQL并没有启动&#xff0c;查看日志如下&#xff1a; 2018-04-13T06:27:24.608793…

Flink CDC 3.4 发布, 优化高频 DDL 处理,支持 Batch 模式,新增 Iceberg 支持

引言 Apache Flink 社区很开心地宣布&#xff0c;在经过4个月的版本开发之后&#xff0c;Flink CDC 3.4.0 版本已经正式发布。Flink CDC 是流行的流式数据集成框架&#xff0c;CDC 3.4.0 版本强化了框架对于高频表结构变更的支持&#xff0c;框架支持了 batch 执行模式&#x…

Flink SQL、Hudi 、Doris在数据上的组合应用

Flink SQL、Hudi 和 Doris 是大数据领域中不同定位的技术组件&#xff0c;各自解决不同的问题&#xff0c;以下从核心定位、关键特性和典型场景三个维度展开说明&#xff1a; 1. Flink SQL&#xff1a;流批统一的实时计算引擎 核心定位&#xff1a;Flink 是 Apache 顶级的流批…

如何实现RTSP和RTMP低至100-200ms的延迟:直播SDK的技术突破

在实时音视频传输中&#xff0c;低延迟是直播应用的核心技术要求之一。无论是在线教育、远程医疗&#xff0c;还是实时互动直播&#xff0c;延迟过大会影响用户体验&#xff0c;甚至导致应用无法正常使用。大牛直播SDK&#xff08;SmartMediaKit&#xff09;在RTSP和RTMP播放器…

upload-labs通关笔记-第15关 文件上传之getimagesize绕过(图片马)

目录 一、图片马 二、文件包含 三、文件包含与图片马 四、图片马制作方法 五、源码分析 六、制作图片马 1、创建脚本并命名为test.php 2、准备制作图片马的三类图片 3、 使用copy命令制作图片马 七、渗透实战 1、GIF图片马渗透 &#xff08;1&#xff09;上传gif图…

基于区块链的茅台酒溯源系统:设计方案、应用实例及未来展望

一、项目背景与需求 茅台酒&#xff0c;作为中国白酒的瑰宝&#xff0c;以其深厚的历史底蕴和独特的酿造工艺享誉全球。然而&#xff0c;市场上假冒伪劣产品的泛滥&#xff0c;不仅严重损害了消费者的权益&#xff0c;也对茅台酒的品牌声誉造成了巨大冲击。为了解决这一问题&a…

openCV1.1 Mat对象

imread(“D:\souse\duoxile.jpg”, IMREAD_COLOR); 功能: 从指定路径读取图像文件并解码为OpenCV的Mat对象 第一个参数: 文件路径 类型: const string&描述: 要读取的图像文件的绝对或相对路径示例: “D:\souse\duoxile.jpg” 或 “./images/test.png”第二个参数: 读取模…

java day14

接昨天&#xff0c;响应 响应 就是我们在处理请求的时候&#xff0c;里面的return 其实方法里面写的return的返回平常的什么字符串啊什么等等&#xff1b;这些东西都是直接返回&#xff1b;如果是一个对象的话&#xff0c;我们会按json的格式返回&#xff1b; 这些都依赖于一…

【软件设计师】计算机网络考点整理

以下是软件设计师考试中 ​​计算机网络​​ 的核心考点总结&#xff0c;帮助您高效备考&#xff1a; ​​一、网络体系结构与协议​​ ​​OSI七层模型 & TCP/IP四层模型​​ 各层功能&#xff08;物理层-数据链路层-网络层-传输层-会话层-表示层-应用层&#xff09;对应协…

基于深度学习的工件检测系统设计与实现

在工业自动化领域&#xff0c;工件检测一直是提高生产效率和产品质量的关键环节。传统的人工检测方法不仅效率低下&#xff0c;而且容易受到主观因素的影响&#xff0c;导致误判率较高。随着深度学习技术的飞速发展&#xff0c;基于图像识别的自动检测系统逐渐成为研究热点。今…

IIS入门指南:原理、部署与实战

引言&#xff1a;Web服务的基石 在Windows Server机房中&#xff0c;超过35%的企业级网站运行在IIS&#xff08;Internet Information Services&#xff09;之上。作为微软生态的核心Web服务器&#xff0c;IIS不仅支撑着ASP.NET应用的运行&#xff0c;更是Windows Server系统管…

Linux周测(一)

提示&#xff1a;学习一周了&#xff0c;来检验一下成果吧 文章目录 技术部分&#xff0c;满分100分。 1.如何在Linux系统中查看当前登录的所有用户信息&#xff1f; w或者who或者last 2.请写出在Linux系统中创建一个新用户“testuser”的命令&#xff0c;并指定其家目录为“/h…

构建下一代AI智能体:基于Spring AI的多轮对话应用

构建下一代AI智能体&#xff1a;基于Spring AI的多轮对话应用 前言 大模型时代&#xff0c;AI应用开发已不再是遥不可及的技术。通过合理设计的Prompt工程和对话架构&#xff0c;开发者可以快速构建具备持续记忆能力的AI智能体。本文将重点介绍如何基于Spring AI框架打造可持…

查看mysql配置文件my.cnf的位置

3.删除mysql相关文件 想要完全卸载mysql&#xff0c;不仅要卸载应用&#xff0c;配置文件及相关文件也需要一一清除&#xff0c;还原环境配置&#xff0c;减少一些麻烦。 sudo rm -rf /usr/local/mysql sudo rm -rf /etc/my.cnf sudo rm -rf /var/db/mysql sudo rm -rf /var/…

【从基础到模型网络】深度学习-语义分割-基础

语义分割在深度学习与人工智能领域占据重要地位。它是计算机视觉的核心任务之一&#xff0c;能够将图像像素级地划分为不同语义类别&#xff0c;为理解图像内容提供关键支持。在自动驾驶中&#xff0c;可精准识别道路、车辆、行人等元素&#xff0c;保障行车安全&#xff1b;在…

C++:array容器

array容器是序列容器&#xff0c;它的特点是&#xff1a;静态&#xff0c;固定数目。可以看作更安全的数组。 它还有一些成员函数&#xff0c;如begin&#xff08;&#xff09;&#xff1a;返回指向容器中第一个元素的随机访问迭代器。 #include<iostream>//数组容器 #…

2025年度消费新潜力白皮书470+份汇总解读|附PDF下载

原文链接&#xff1a;https://tecdat.cn/?p42178 过去一年&#xff0c;消费市场在政策驱动与技术迭代中呈现结构性变革。社零总额达487,895亿元&#xff0c;实物商品网零额占比27%&#xff0c;线上渠道成为增长引擎。本报告从食品饮料、美妆护肤、家电数码、服饰户外四大核心领…

[Web服务器对决] Nginx vs. Apache vs. LiteSpeed:2025年性能、功能与适用场景深度对比

更多服务器知识&#xff0c;尽在hostol.com 当你准备为你的网站或应用程序选择一款 Web 服务器软件时&#xff0c;就像是为你的“超级跑车”选择一款合适的“引擎”——它将直接决定你的“座驾”能跑多快、多稳、以及能适应什么样的“路况”&#xff08;工作负载&#xff09;。…

一款适配国内的视频软件,畅享大屏与局域网播放

软件介绍 今天要给大家安利一款超强视频播放软件——MXPlayer。它的解码实力堪称一绝&#xff0c;市面上不管是常见的 MP4、MKV 格式&#xff0c;还是对播放设备要求极高的超高清 4K、HDR 视频&#xff0c;甚至那些鲜为人知的冷门格式&#xff0c;它统统都能流畅播放&#xff…