Node.js 中的 RSA 加密、解密、签名与验证详解

引言

在现代的网络通信中,数据安全显得尤为重要。RSA加密算法因其非对称的特性,广泛应用于数据的加密、解密、签名和验证等安全领域。本文将详细介绍RSA算法的基本原理,并结合Node.js环境,展示如何使用内置的crypto模块和第三方库node-rsa来实现RSA的加密、解密、签名和验证。

RSA算法原理

RSA算法是一种非对称加密算法,由Ron Rivest、Adi Shamir和Leonard Adleman于1977年提出。它的安全性基于大数分解的困难性。RSA算法涉及到三个关键的概念:密钥对生成、加密和解密。

密钥对生成

  1. 选择两个大的质数pq
  2. 计算n = p * qn用于构成公钥和私钥。
  3. 计算φ(n) = (p-1) * (q-1)φ(n)用于选择公钥指数e和私钥指数d
  4. 选择一个小于φ(n)的整数e,通常取65537(2^16 + 1),因为其具有良好的数学性质。
  5. 计算d,使得(e * d) % φ(n) = 1
  6. 公钥为(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 的图解框架

  1. 客户端生成密钥对:客户端使用Node.js的crypto模块生成RSA密钥对,包括一个私钥和一个公钥。
  2. 服务器发送公钥:服务器将其公钥发送给客户端。这可以通过HTTP请求、文件传输或其他通信方式完成。
  3. 客户端接收公钥:客户端接收到服务器的公钥,并将其存储起来,以便用于加密要发送给服务器的数据。
  4. 服务器接收加密数据:客户端使用服务器的公钥对数据进行加密,并将加密后的数据发送给服务器。服务器接收到加密数据。
  5. 服务器使用私钥解密数据:服务器使用其私钥对收到的加密数据进行解密,得到原始数据,并进行处理。处理完成后,服务器可以对响应数据进行加密,并将其发送回客户端。

注意:这个图解是一个简化的表示,实际应用中可能包含更多的步骤和安全措施,例如签名验证、密钥管理和存储等。

使用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加密、解密、签名和验证变得简单易行。开发者可以根据项目需求和环境选择合适的工具进行数据加密和安全保护。

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

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

相关文章

基于 LSTM 模型的古诗词自动生成算法实现及系统实现

近年来,研究者在利用循环神经网络(Recurrent Neural Network,RNN)进行古诗自动生成方面取得了显著的效果。但 RNN 存在梯度问题,导致处理时间跨度较长的序列时 RNN 并不具备长期记忆存储功能。随后,出现的基…

【架构方法论(一)】架构的定义与架构要解决的问题

文章目录 一. 架构定义与架构的作用1. 系统与子系统2. 模块与组件3. 框架与架构4. 重新定义架构:4R 架构 二、架构设计的真正目的-别掉入架构设计的误区1. 是为了解决软件复杂度2. 简单的复杂度分析案例 三. 案例思考 本文关键字 架构定义 架构与系统的关系从业务逻…

企业linux-堡垒机与跳板机测试案例-6140字详谈

在开始今天内容前,小编先把专栏前面学的Linux命令(部分)做了思维导图帮助各位平时的学习: 场景: 运维人员管理三台机器,通过远程连接工具连接上三台机器,也知道这三台机器root密码&#xff0c…

【Java探索之旅】掌握数组操作,轻松应对编程挑战

🎥 屿小夏 : 个人主页 🔥个人专栏 : Java编程秘籍 🌄 莫道桑榆晚,为霞尚满天! 文章目录 📑前言一、数组巩固练习1.1 数组转字符串1.2 数组拷贝1.3 求数组中的平均值1.4 查找数组中指…

Windows版Apache 2.4.59解压直用(免安装-绿色-项目打包直接使用)

windows下Apache分类 Apache分为 安装版和解压版 安装版: 安装方便,下一步------下一步就OK了,但重装系统更换环境又要重新来一遍,会特别麻烦 解压版(推荐): 这种方式(项目打包特别方便&#x…

力扣哈哈哈哈

public class MyStack {int top;Queue<Integer> q1;Queue<Integer> q2;public MyStack() {q1new LinkedList<Integer>();q2new LinkedList<Integer>();}public void push(int x) {q2.offer(x);//offer是入队方法while (!q1.isEmpty()){q2.offer(q1.pol…

HPTNet:为点云提取表面特征

论文题目&#xff1a;High-Performance Feature Extraction Network for Point Cloud Semantic Segmentation 论文地址&#xff1a;https://ieeexplore.ieee.org/abstract/document/10474110 文章目录 1. 平面几何特征的提取2. 几何和语义特征的分开处理3. Transformer模块4. 结…

MySQL基础知识——MySQL事务

事务背景 什么是事务&#xff1f; 一组由一个或多个数据库操作组成的操作组&#xff0c;能够原子的执行&#xff0c;且事务间相互独立&#xff1b; 简单来说&#xff0c;事务就是要保证一组数据库操作&#xff0c;要么全部成功&#xff0c;要么全部失败。 注&#xff1a;MyS…

代码随想录算法训练营第一天 | 704. 二分查找 | 27. 移除元素

704. 二分查找 int search(int* nums, int numsSize, int target) {int left 0, right numsSize, mid;while (left < right) {mid left (right -left) / 2;if (nums[mid] < target) {left mid 1;} else if (nums[mid] > target) {right mid;} else {return mid…

CMMI认证是什么?如何确定CMMI认证的目标和范围

CMMI&#xff08;Capability Maturity Model Integration&#xff09;认证是一种用于评估和改进组织软件和项目管理过程的框架。它由美国国防部软件工程所&#xff08;SEI&#xff09;开发&#xff0c;旨在帮助组织提高其软件和项目管理的成熟度水平。 CMMI认证的意义在于&…

哪里有su材质库免费下载?

su材质库是一套草图大师的通用材质大全&#xff0c;包含多种不同类型的材质包和材质贴图&#xff0c;使得设计师能够轻松在电脑上进行直观的构思。对于需要免费下载su材质库的用户&#xff0c;可以尝试通过以下途径获取。 1. 官方网站查找&#xff1a;许多软件都会在官网上提供…

第十六篇:springboot案例

文章目录 一、准备工作1.1 需求说明1.2 环境搭建1.3 开发规范1.4 思路 二、部门管理2.1 查询部门2.2 删除部门2.3 新增部门2.4 修改部门2.5 RequestMapping 三、员工管理3.1 分页查询3.2 删除员工3.3 新增员工3.3.1 新增员工3.3.2 文件上传 3.4 修改员工3.4.1 页面回显3.4.2 修…

【数据结构】-- 栈和队列

&#x1f308; 个人主页&#xff1a;白子寰 &#x1f525; 分类专栏&#xff1a;python从入门到精通&#xff0c;魔法指针&#xff0c;进阶C&#xff0c;C语言&#xff0c;C语言题集&#xff0c;C语言实现游戏&#x1f448; 希望得到您的订阅和支持~ &#x1f4a1; 坚持创作博文…

二十一.订单分析RFM模型

目录 1.数据读取 2.数据清洗 3.可视化分析 做图吧 4.RFM模型 本次数据条数为: 51101 import pandas as pd import numpy as np 1.数据读取 #读取文件 df_data pd.read_csv("../data/dataset.csv",encoding"gbk") df_data#因为列标签都是英文,这里我…

通讯录的实现(顺序表)

前言&#xff1a;上篇文章我们讲解的顺序表以及顺序表的具体实现过程&#xff0c;那么我们的顺序表在实际应用中又有什么作用呢&#xff1f;今天我们就基于顺序表来实现一下通讯录。 目录 一.准备工作 二.通讯录的实现 1.通讯录的初始化 2.插入联系人 3.删除联系人 4.…

手机副业赚钱秘籍:让你的手机变成赚钱利器

当今社会&#xff0c;智能手机已然成为我们生活不可或缺的一部分。随着技术的飞速进步&#xff0c;手机不再仅仅是通讯工具&#xff0c;而是化身为生活伴侣与工作助手。在这个信息爆炸的时代&#xff0c;我们时常会被一种焦虑感所困扰&#xff1a;如何能让手机超越消磨时光的定…

TinyEMU源码分析之访存处理

TinyEMU源码分析之访存处理 1 访存指令介绍2 指令译码3 地址转换3.1 VA与PA3.2 VA转PA 4 判断地址空间范围5 执行访存操作5.1 访问RAM内存5.2 访问非RAM&#xff08;设备&#xff09;内存 6 访存处理流程图 本文属于《 TinyEMU模拟器基础系列教程》之一&#xff0c;欢迎查看其…

【AI】什么是Ai Agent

什么是AI Agent&#xff1f; AI Agent是指人工智能代理&#xff08;Artificial Intelligence Agent&#xff09;是一种能够感知环境进行自主理解&#xff0c;进行决策和执行动作的智能体。AI Agent具备通过独立思考、调用工具逐步完成给定目标的能力。不同于大模型的区别在于&…

OpenHarmony实战开发-如何使用屏幕属性getDefaultDisplaySync、getCutoutInfo接口实现适配挖孔屏。

介绍 本示例介绍使用屏幕属性getDefaultDisplaySync、getCutoutInfo接口实现适配挖孔屏。该场景多用于沉浸式场景下。 效果图预览 使用说明 1.加载完成后顶部状态栏时间和电量显示位置规避了不可用区域。 实现思路 1.通过setWindowLayoutFullScreen、setWindowSystemBarEn…

代码随想录训练营

Day23代码随想录 669.修剪二叉搜索树 1.题目描述 给你二叉搜索树的根节点 root &#xff0c;同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树&#xff0c;使得所有节点的值在[low, high]中。修剪树 不应该 改变保留在树中的元素的相对结构 (即&#xff0c;如果没有…