Node.js 中的 RSA 加密、解密、签名与验证详解
引言
在现代的网络通信中,数据安全显得尤为重要。RSA加密算法因其非对称的特性,广泛应用于数据的加密、解密、签名和验证等安全领域。本文将详细介绍RSA算法的基本原理,并结合Node.js环境,展示如何使用内置的crypto模块和第三方库node-rsa来实现RSA的加密、解密、签名和验证。
RSA算法原理
RSA算法是一种非对称加密算法,由Ron Rivest、Adi Shamir和Leonard Adleman于1977年提出。它的安全性基于大数分解的困难性。RSA算法涉及到三个关键的概念:密钥对生成、加密和解密。
密钥对生成
- 选择两个大的质数
p和q。 - 计算
n = p * q,n用于构成公钥和私钥。 - 计算
φ(n) = (p-1) * (q-1),φ(n)用于选择公钥指数e和私钥指数d。 - 选择一个小于
φ(n)的整数e,通常取65537(2^16 + 1),因为其具有良好的数学性质。 - 计算
d,使得(e * d) % φ(n) = 1。 - 公钥为
(n, e),私钥为(n, d)。
加密和解密
- 加密:假设明文为
M,密文C计算公式为C ≡ M^e (mod n)。 - 解密:密文
C解密回明文M计算公式为M ≡ C^d (mod n)。
签名和验证
- 签名:发送方使用私钥对消息
M进行签名,生成签名S ≡ M^d (mod n)。 - 验证:接收方使用发送方的公钥验证签名
S,如果S^e (mod n) == M,则签名有效。
Node.js 使用 RSA 的图解框架

- 客户端生成密钥对:客户端使用Node.js的
crypto模块生成RSA密钥对,包括一个私钥和一个公钥。 - 服务器发送公钥:服务器将其公钥发送给客户端。这可以通过HTTP请求、文件传输或其他通信方式完成。
- 客户端接收公钥:客户端接收到服务器的公钥,并将其存储起来,以便用于加密要发送给服务器的数据。
- 服务器接收加密数据:客户端使用服务器的公钥对数据进行加密,并将加密后的数据发送给服务器。服务器接收到加密数据。
- 服务器使用私钥解密数据:服务器使用其私钥对收到的加密数据进行解密,得到原始数据,并进行处理。处理完成后,服务器可以对响应数据进行加密,并将其发送回客户端。
注意:这个图解是一个简化的表示,实际应用中可能包含更多的步骤和安全措施,例如签名验证、密钥管理和存储等。
使用Node.js实现RSA
环境准备
在Node.js中,我们可以使用内置的crypto模块或第三方库node-rsa来实现RSA的功能。
使用内置crypto模块
Node.js的crypto模块提供了丰富的加密功能,包括RSA的加密和解密。
const crypto = require('crypto');// 生成RSA密钥对
const { privateKey, publicKey } = crypto.generateKeyPairSync('rsa', {modulusLength: 2048,publicKeyEncoding: {type: 'spki',format: 'pem'},privateKeyEncoding: {type: 'pkcs8',format: 'pem',cipher: 'aes-256-cbc',passphrase: 'your-secret-passphrase'}
});// 加密数据
const data = 'Hello RSA!';
const encrypted = crypto.publicEncrypt({key: publicKey,padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,oaepHash: 'sha256'
}, Buffer.from(data));// 解密数据
const decrypted = crypto.privateDecrypt({key: privateKey,padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,oaepHash: 'sha256'
}, encrypted);// RSA签名
const sign = crypto.createSign('SHA256');
sign.update(data);
const signature = sign.sign(privateKey, 'base64');// RSA签名验证
const verify = crypto.createVerify('SHA256');
verify.update(data);
const result = verify.verify(publicKey, signature, 'base64');
使用node-rsa库
node-rsa是一个纯JavaScript实现的RSA加密库,不需要依赖于任何外部的加密库。
const NodeRSA = require('node-rsa');// 创建NodeRSA实例
const nodeRSA = new NodeRSA({ b: 2048 }); // b是密钥长度// 生成密钥对
const keyPair = nodeRSA.generateKeyPair();// 加密数据
const encrypted = nodeRSA.encrypt(data, 'base64', 'public');// 解密数据
const decrypted = nodeRSA.decrypt(encrypted, 'utf8', 'private');// RSA签名
const signature = nodeRSA.sign('SHA256', data, 'base64', 'private');// RSA签名验证
const verifyResult = nodeRSA.verify('SHA256', data, signature, 'public');
如何使用 Node.js 进行 RSA 密钥的管理和存储?
在Node.js中进行RSA密钥的管理和存储是一个重要的安全实践。正确的管理存储机制可以确保私钥的安全性和公钥的可用性。以下是一些关于如何使用Node.js进行RSA密钥管理和存储的建议和方法:
1. 生成密钥对
首先,你需要生成RSA密钥对。在Node.js中,你可以使用crypto模块来生成密钥对。
const crypto = require('crypto');const { privateKey, publicKey } = crypto.generateKeyPairSync('rsa', {modulusLength: 2048,publicKeyEncoding: {type: 'spki',format: 'pem',},privateKeyEncoding: {type: 'pkcs8',format: 'pem',cipher: 'aes-256-cbc',passphrase: 'your-secret-passphrase'},
});
2. 存储密钥
生成密钥对后,你需要将它们安全地存储起来。以下是一些存储方法:
2.1 文件系统
将密钥存储在文件系统中是一种常见的做法。你可以将公钥和私钥写入不同的文件,并确保这些文件的权限设置得当,以防止未授权访问。
const fs = require('fs');// 将私钥写入文件
fs.writeFileSync('private_key.pem', privateKey);// 将公钥写入文件
fs.writeFileSync('public_key.pem', publicKey);
确保只有运行Node.js应用的用户(通常是web服务器用户)有权限访问这些文件。
2.2 环境变量
对于私钥,你可以将其存储在环境变量中,这样可以避免将其硬编码在代码中或存储在文件系统中。
process.env.PRIVATE_KEY = privateKey;
然后,你可以使用process.env.PRIVATE_KEY来访问私钥。
2.3 密钥管理服务
对于生产环境,建议使用专门的密钥管理服务(如AWS KMS、Google Cloud KMS或HashiCorp Vault)来存储和管理密钥。这些服务提供了额外的安全措施,如硬件安全模块(HSM)保护、访问控制和审计日志。
3. 加载密钥
当你需要使用密钥时,你可以从存储位置加载它们。
3.1 从文件加载
如果你将密钥存储在文件中,可以使用fs模块来加载它们。
const fs = require('fs');// 从文件加载私钥
const privateKey = fs.readFileSync('private_key.pem', 'utf8');// 从文件加载公钥
const publicKey = fs.readFileSync('public_key.pem', 'utf8');
3.2 从环境变量加载
如果你将密钥存储在环境变量中,可以直接使用process.env来访问它们。
const privateKey = process.env.PRIVATE_KEY;
const publicKey = process.env.PUBLIC_KEY;
3.3 从密钥管理服务加载
如果你使用密钥管理服务,通常会有相应的SDK或API来从服务中检索密钥。
4. 安全考虑
- 保护私钥:确保私钥不会被泄露。使用强密码短语保护私钥,并限制对私钥文件的访问权限。
- 定期轮换密钥:定期更换密钥可以减少因密钥泄露带来的风险。
- 备份密钥:对密钥进行安全备份,以防原始密钥丢失或损坏。
- 使用最新算法:使用最新的加密算法和足够长的密钥长度来抵御计算力的增长。
通过遵循这些最佳实践,你可以确保在Node.js应用中安全地管理和存储RSA密钥。
使用 Node.js 和 RSA 加密进行安全通信的实战案例
例子:模拟一个简单的客户端和服务器之间的安全通信过程,其中服务器使用RSA加密来保护传输的数据。这个例子将展示如何生成RSA密钥对、加密数据、发送加密数据以及服务器端解密数据的过程。
步骤 1: 生成RSA密钥对
首先,我们需要生成一对RSA密钥。在这个例子中,我们将使用Node.js的crypto模块来生成密钥对。
const crypto = require('crypto');// 生成RSA密钥对
const { privateKey, publicKey } = crypto.generateKeyPairSync('rsa', {modulusLength: 2048,publicKeyEncoding: {type: 'spki',format: 'pem',},privateKeyEncoding: {type: 'pkcs8',format: 'pem',},
});console.log('Public Key:', publicKey);
console.log('Private Key:', privateKey);
步骤 2: 客户端加密数据
客户端将使用服务器的公钥来加密要发送的数据。这里我们假设数据是一个简单的文本消息。
// 客户端代码
const crypto = require('crypto');// 服务器的公钥
const publicKey = '...'; // 从步骤1中获取的公钥// 要发送的数据
const data = 'Secret Message';// 使用公钥加密数据
const encryptedData = crypto.publicEncrypt({key: publicKey,padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,oaepHash: 'sha256',
}, Buffer.from(data)).toString('base64');console.log('Encrypted Data:', encryptedData);
步骤 3: 服务器端接收并解密数据
服务器端接收到加密数据后,将使用自己的私钥来解密数据。
// 服务器端代码
const crypto = require('crypto');// 服务器的私钥
const privateKey = '...'; // 从步骤1中获取的私钥// 客户端发送的加密数据
const encryptedData = '...'; // 从客户端接收到的加密数据// 使用私钥解密数据
const decryptedData = crypto.privateDecrypt({key: privateKey,padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,oaepHash: 'sha256',
}, Buffer.from(encryptedData, 'base64')).toString('utf8');console.log('Decrypted Data:', decryptedData);
步骤 4: 确保数据的完整性和认证
为了确保数据的完整性和认证,我们可以使用RSA签名来验证数据是否被篡改,并且确实是由预期的发送方发送的。
// 客户端签名数据
const sign = crypto.createSign('SHA256');
sign.update(data);
const signature = sign.sign(privateKey, 'base64');// 客户端发送数据和签名给服务器// 服务器端验证签名
const verify = crypto.createVerify('SHA256');
verify.update(data);
const isValidSignature = verify.verify(publicKey, signature, 'base64');if (isValidSignature) {console.log('Signature is valid, data is authentic.');
} else {console.log('Signature is invalid, data may have been tampered.');
}
通过这个实战案例,我们展示了如何在Node.js中使用RSA加密来保护客户端和服务器之间的通信。我们生成了RSA密钥对,客户端使用服务器的公钥加密数据,然后服务器使用自己的私钥解密数据。此外,我们还使用了RSA签名来确保数据的完整性和认证。这种方法可以有效地防止数据在传输过程中被窃听或篡改,从而提高通信的安全性。
总结
RSA算法作为一种非对称加密技术,在保障数据传输安全方面发挥着重要作用。Node.js提供了内置的crypto模块和第三方库node-rsa,使得在Node.js环境中实现RSA加密、解密、签名和验证变得简单易行。开发者可以根据项目需求和环境选择合适的工具进行数据加密和安全保护。
相关文章:
Node.js 中的 RSA 加密、解密、签名与验证详解
引言 在现代的网络通信中,数据安全显得尤为重要。RSA加密算法因其非对称的特性,广泛应用于数据的加密、解密、签名和验证等安全领域。本文将详细介绍RSA算法的基本原理,并结合Node.js环境,展示如何使用内置的crypto模块和第三方库…...
vue+element作用域插槽
作用域插槽的样式由父组件决定,内容却由子组件控制。 在el-table使用作用域插槽 <el-table><el-table-column slot-scope" { row, column, $index }"></el-table-column> </el-table>在el-tree使用作用域插槽 <el-tree>…...
MUSA模型
MUSA模型在软件可靠性工程中起到的作用是估计软件的故障/失效数量和故障率。具体来说,MUSA模型包括基本模型和对数模型。 MUSA基本模型假设故障发生的时间间隔服从参数为lambda的指数分布。在这个模型中,当故障被检测到时,发生故障的部分会被…...
avicat连接异常,错误编号2059-authentication plugin…
错误原因为密码方式不对,具体可自行百度 首先管理员执行cmd进入 mysql安装目录 bin下边 我的是C:\Program Files\MySQL\MySQL Server 8.2\bin> 执行 mysql -u -root -p 然后输入密码 123456 进入mysql数据库 use mysql 执行 ALTER USER rootlocalhost IDE…...
阿里云云效CI/CD配置
1.NODEJS项目流水线配置(vue举例) nodejs构建配置 官方教程 注意:下图的dist是vue项目打包目录名称,根据实际名称配置 # input your command here cnpm cache clean --force cnpm install cnpm run build 主机部署配置 rm -rf /home/vipcardmall/frontend/ mkdir -p /home/…...
个人开发者,Spring Boot 项目如何部署
今天给大家分享一下,作为个人开发者,Spring Boot 项目是如何部署的。 环境介绍 Linux docker docker-compose 目录结构 erwin-windrunner - backups - data - jars - build-docker-compose.sh - docker-compose.yml - Dockerfile文件 Dockerfile …...
【Spring进阶系列丨第九篇】基于XML的面向切面编程(AOP)详解
文章目录 一、基于XML的AOP1.1、打印日志案例1.1.1、beans.xml中添加aop的约束1.1.2、定义Bean 1.2、定义记录日志的类【切面】1.3、导入AOP的依赖1.4、主配置文件中配置AOP1.5、测试1.6、切入点表达式1.6.1、访问修饰符可以省略1.6.2、返回值可以使用通配符,表示任…...
学习记录:转发和重定向
转发(Forward)和重定向(Redirect)是两种不同的 Web 请求处理方式,它们在功能和行为上有着显著的区别。 区别 转发(Forward): 服务器内部跳转:转发是服务器内部的行为&…...
实现(图像、视频等)数据上云存储
实现(图像、视频等)数据上云存储 实现(图像、视频等)数据上云存储通常涉及以下几个步骤: 选择云存储服务商: 根据您的需求、预算、地域覆盖、数据安全性、服务稳定性等因素,选择一家合适的云存储…...
LeetCode 454.四数相加II
LeetCode 454.四数相加II 1、题目 题目链接:454. 四数相加 II - 力扣(LeetCode) 给你四个整数数组 nums1、nums2、nums3 和 nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足: 0 <…...
GoogleNet网络训练集和测试集搭建
测试集和训练集都是在之前搭建好的基础上进行修改的,重点记录与之前不同的代码。 还是使用的花分类的数据集进行训练和测试的。 一、训练集 1、搭建网络 设置参数:使用辅助分类器,采用权重初始化 net GoogleNet(num_classes5, aux_logi…...
将数字状态码在后台转换为中文状态
这是我们的实体类 可以看出我们的状态status是2如过返回到前端我们根本不知道2代表的是什么,所以我们需要再这里将数字转换成能看懂的中文状态,首先我们创建一个枚举类 先将我们状态码所对应的中文状态枚举出来,然后创建一个静态方法&#…...
2017NOIP普及组真题 4. 跳房子
线上OJ: 一本通:http://ybt.ssoier.cn:8088/problem_show.php?pid1417\ 核心思想 首先、本题中提到 “ 至少 要花多少金币改造机器人,能获得 至少 k分 ”。看到这样的话语,基本可以考虑要使用 二分答案。 那么,本题中…...
网络与 Internet因特网的基本概念
目录 网络Internet (互联网或互连网)Internet(因特网)待续、更新中 网络 指将分布在不同地理位置的、相同或不同类型的网络通过网络互连设备(中继器、网桥、路由器或网关等)相互连接,形成一个范…...
vue-router 中 router-link 与 a 标签的区别
文章目录 前言 a标签定义 router-link定义 总结 前言 vue-router 中 router-link 与 a 标签的区别 a标签定义 <a> 标签定义超链接,用于从一张页面链接到另一张页面。 从一张页面跳转到另一张页面,但从这里来说就违背了多视图的单页Web应用这个…...
MySQL基础知识——MySQL事务
事务背景 什么是事务? 一组由一个或多个数据库操作组成的操作组,能够原子的执行,且事务间相互独立; 简单来说,事务就是要保证一组数据库操作,要么全部成功,要么全部失败。 注:MyS…...
【架构方法论(一)】架构的定义与架构要解决的问题
文章目录 一. 架构定义与架构的作用1. 系统与子系统2. 模块与组件3. 框架与架构4. 重新定义架构:4R 架构 二、架构设计的真正目的-别掉入架构设计的误区1. 是为了解决软件复杂度2. 简单的复杂度分析案例 三. 案例思考 本文关键字 架构定义 架构与系统的关系从业务逻…...
基于springboot实现人口老龄化社区服务与管理系统项目【项目源码+论文说明】计算机毕业设计
基于springboot实现人口老龄化社区服务与管理系统演示 摘要 随着信息技术在管理上越来越深入而广泛的应用,管理信息系统的实施在技术上已逐步成熟。本文介绍了人口老龄化社区服务与管理平台的开发全过程。通过分析人口老龄化社区服务与管理平台方面的不足ÿ…...
代码随想录算法训练营第三十七天| LeetCode 738.单调递增的数字、总结
一、LeetCode 738.单调递增的数字 题目链接/文章讲解/视频讲解:https://programmercarl.com/0738.%E5%8D%95%E8%B0%83%E9%80%92%E5%A2%9E%E7%9A%84%E6%95%B0%E5%AD%97.html 状态:已解决 1.思路 如何求得小于等于N的最大单调递增的整数?98&am…...
C++动态内存管理 解剖new/delete详细讲解(operator new,operator delete)
讨厌抄我作业和不让我抄作业的人 讨厌插队和不让我插队的人 讨厌用我东西和不让我用东西的人 讨厌借我钱和不借给我钱的人 讨厌开车加塞和不让我加塞的人 讨厌内卷和打扰我内卷的人 一、C中动态内存管理 1.new和delete操作内置类型 2.new和delete操作自定义类型 二、operat…...
利用最小二乘法找圆心和半径
#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...
黑马Mybatis
Mybatis 表现层:页面展示 业务层:逻辑处理 持久层:持久数据化保存 在这里插入图片描述 Mybatis快速入门 使用React Native…...
C++ 基础特性深度解析
目录 引言 一、命名空间(namespace) C 中的命名空间 与 C 语言的对比 二、缺省参数 C 中的缺省参数 与 C 语言的对比 三、引用(reference) C 中的引用 与 C 语言的对比 四、inline(内联函数…...
3403. 从盒子中找出字典序最大的字符串 I
3403. 从盒子中找出字典序最大的字符串 I 题目链接:3403. 从盒子中找出字典序最大的字符串 I 代码如下: class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...
【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分
一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计,提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合:各模块职责清晰,便于独立开发…...
网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...
10-Oracle 23 ai Vector Search 概述和参数
一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI,使用客户端或是内部自己搭建集成大模型的终端,加速与大型语言模型(LLM)的结合,同时使用检索增强生成(Retrieval Augmented Generation &#…...
