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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

RT-Thread 多级目录 scons 构建

前言 RT-Thread 默认使用 scons 进行工程的构建,虽然 RT-Thread BSP 中的 hello world 例程比较简单,实际项目开发,可能源码的工程会由多级目录,如何让多级的目录参与构建? scons 构建时,除了依赖工程的根…

libbpf-bootstrap库的代码结构介绍(用户层接口介绍),编译链接语句详细介绍,.skel.h文件介绍+示例,bpf程序的后续处理+文件关系总结

目录 libbpf-bootstrap 代码结构介绍 用户层函数 编译 查看 生成内核层的.o文件 第一模块 第二模块 第三模块 第四模块 第五模块 生成辅助文件(.skel.h) 介绍 示例 生成代码层的.o文件 第一模块 第二模块 第三模块 链接出可执行文件 后续总结 libbpf-bootst…

云服务器web环境之mariadb

1.安装mariadb服务 yum install mariadb-server 启动mariadb服务 systemctl start mariadb.service 输入mysql就能使用数据库了。 2.服务相关操作 systemctl stop mariadb.service systemctl restart mariadb.service 2.配置开机自启动 systemctl enable mariadb.serv…

AI克隆语音(基于GPT-SoVITS)

概述 使用GPT-SoVITS训练声音模型,实现文本转语音功能。可以模拟出语气,语速。如果数据质量足够高,可以达到非常相似的结果。相比于So-VITS-SVC需要的显卡配置更低,数据集更小(我的笔记本NVIDIA GeForce RTX 4050 Lap…

深入剖析MongoDB集群架构设计

目录 一、MongoDB集群架构介绍 1.1 主从复制 1.2 副本集 1.3 分片集群 二、副本集 3.1 主节点选举 3.2 oplog 3.2 主从同步 三、分片集群 3.1 分片策略 3.2 分片键的选择 3.3 何时选择分片集群 四、总结 一、MongoDB集群架构介绍 MongoDB 有三种集群架构模式,分…

(七)PostgreSQL的用户管理

PostgreSQL的用户管理 1 创建用户(角色) CREATE USER现在是CREATE ROLE的别名。唯一的区别是,当命令的拼写为CREATE USER时,默认情况下会使用LOGIN,而当命令拼写为CREATE ROLE时会使用NOLOGIN。 官方文档&#xff1a…

系统架构最佳实践 -- 统一身份认证系统

目录 1.系统架构设计: 2.用户认证与授权: 3.用户身份管理: 4.安全性保障: 5.日志记录与审计: 6.高可用性与容错性: 7.用户体验优化: 随着互联网的快速发展和应用的普及,人们在…

边缘计算【智能+安全检测】系列教程--使用OpenCV+GStreamer实现真正的硬解码,完全消除马赛克

通过现有博客的GST_URL = "rtspsrc location=rtsp://admin:abcd1234@192.168.1.64:554/h264/ch01/main/av_stream latency=150 ! rtph264depay ! avdec_h264 ! videorate ! videoconvert ! appsink sync=false" GStreamer的解码方式解码,大多情况应该存在上图马赛克…

基于机器学习的人脸发型推荐算法研究与应用实现

1.摘要 本文主要研究内容是开发一种发型推荐系统,旨在识别用户的面部形状,并根据此形状推荐最适合的发型。首先,收集具有各种面部形状的用户照片,并标记它们的脸型,如长形、圆形、椭圆形、心形或方形。接着构建一个面部…

STM32之DHT11温湿度传感器

目录 一 DHT11温湿度传感器简介 1.1 传感器特点 1.2 传感器特性 1.3 传感器引脚说明 二 测量原理及方法 2.1 典型应用电路 2.2 单线制串行简介 2.2.1 串行接口 (单线双向) 2.2.2 数据示例 2.3 通信时序 三 单片机简介 3.1 STM32F103C8T6最小系统板 四 接线说明 …

LLM-大模型演化分支树、GPT派发展阶段及训练流程图、Infini-Transformer说明

大模型是怎么演进的? Encoder Only: 对应粉色分支,即BERT派,典型模型: BERT 自编码模型(Autoencoder Model):通过重建句子来进行预训练,通常用于理解任务,如文本分类和阅…

架构师系列-搜索引擎ElasticSearch(五)- 索引设计

索引创建后,要非常谨慎,创建不好后面会出现各种问题。 索引设计的重要性 索引创建后,索引分片只能通过_split和_shrink 接口对其进行成倍的增加和缩减。 ES的数据是通过_routing分配到各个分片上的,所以本质上不推荐区改变索引的…

记录一下MySQL8版本更改密码规则

#查看当前密码策略 show variables like validate_password%;#修改密码等级为low set global validate_password.policy LOW; #注意MySQL8版本这是点,不是_#修改密码长度为6 set global validate_password.length 6;#查询我的数据库中user表host和user select host,…

【前端面试3+1】16 TCP与UDP的区别、如何清除浮动、哪些原因造成阻塞页面渲染、【相同的树】

一、TCP与UDP的区别 TCP(Transmission Control Protocol)和UDP(User Datagram Protocol)是两种常用的网络传输协议,它们有以下几点区别: 1、连接性: TCP是面向连接的协议,通信双方在…

以太网数据量大小字符串生成方法(可变单位)

0 前言 当我们想显示以太网数据量大小时,往往有个头疼的单位需要处理,单位取小了不一目了然,单位取大了精度太低。本例设计一个函数,将根据以太网数据量大小自动生成单位可变的字符串(KB、MB、GB、TB、PB)…

【大语言模型】基础:TF-IDF

TF-IDF (Term Frequency-Inverse Document Frequency) 是一种用于信息检索与文本挖掘的统计方法,用来评估一个词对于一个文件集或一个语料库中的其中一份文件的重要性。它是一种常用于文本处理和自然语言处理的权重计算技术。 原理 TF-IDF 由两部分组成&#xff1…

Qt:发出一个信号,有多少相关槽函数执行?

返回连接signal的接收者的个数。 因为信号和槽都能作为信号的接收者,同时相同的连接能被建立很多次,接收者的数量和与该信号建立连接的数量相同。 当调用该函数时,你能使用SIGNAL()宏来传递一个特定的信号: if (receivers(SIGNA…

【core analyzer】core analyzer的介绍和安装详情

目录 🌞1. core和core analyzer的基本概念 🌼1.1 coredump文件 🌼1.2 core analyzer 🌞2. core analyzer的安装详细过程 🌼2.1 方式一 简单但不推荐 🌼2.2 方式二 推荐 🌻2.2.1 安装遇到…

Servlet实现常用功能及其他方法

getParameter 获取body或url中指定的key/value值 String classIdreq.getParameter("classId"); getQueryString 获取请求的所有查询参数key,values1 String queryStringreq.getQueryString(); from表单提交 前端通过from表单提交用户名和密码 <!DOCTYPE htm…