JavaScript逆向非对称加密算法
JavaScript逆向非对称加密算法
- 什么是非对称加密
- 非对称加密的两种核心用途
- 主要特点(优点和缺点)
- 混合加密:现实世界中的最佳实践
- 非对称加密算法分类
- RSA
- 算法介绍
- JavaScript 实现
- Python 实现
- 逆向技巧
- 案例
什么是非对称加密
非对称加密,又称为公钥加密 (Public-key Cryptography),其最核心的特点是:
加密和解密使用的是一对不同的密钥:一个公钥 (Public Key) 和一个私钥 (Private Key)。
- 公钥 (Public Key):可以随意公开,任何人都可以获取。
- 私钥 (Private Key):必须由所有者自己严格保管,绝不能泄露。
这对密钥是通过复杂的数学算法生成的,它们之间存在着一种特殊的单向关系:通过公钥加密的数据,只能用对应的私钥解密;通过私钥加密(签名)的数据,也只能用对应的公钥解密(验证)。
非对称加密的两种核心用途
这对神奇的密钥对带来了两种截然不同的、但都极为重要的应用。
-
用于加密(保证机密性)
目标:A 要发送一条只有 B 能看的机密消息。
流程:- B 先生成一对公钥和私钥,并将公钥告诉 A(可以通过任何不安全的渠道,比如邮件、网站)。
- A 使用 B 的公钥来加密消息。
- A 将加密后的密文发送给 B。
- B 收到密文后,使用自己私有的、从未泄露过的私钥来解密,从而得到原始消息。
在这个过程中,即使攻击者截获了 B 的公钥和加密后的密文,也无法解密,因为他没有 B 的私钥。
-
用于数字签名(保证真实性、完整性和不可否认性)
目标:A 要发送一条消息,并向所有人证明这条消息确实是 A 发的,且未被篡改。
流程:- A 先对要发送的消息计算一个哈希值(摘要)。
- A 使用自己的私钥对这个哈希值进行“加密”,这个加密后的结果就是数字签名 (Digital Signature)。
- A 将原始消息和数字签名一起发送出去。
- 接收方(任何人)收到后,执行验证:
a. 使用 A 的公钥对数字签名进行“解密”,得到一个哈希值(我们称之为 H1)。
b. 对收到的原始消息重新计算一次哈希值(我们称之为 H2)。
c. 比较 H1 和 H2 是否完全相等。
如果相等,则证明了两件事:
- 真实性/身份验证:因为只有 A 的私钥才能生成能被 A 的公钥解开的签名,所以证明消息确实来自 A。
- 完整性/不可否认性:因为消息的哈希值能对上,证明消息在传输过程中未被篡改。A 也无法否认自己发送过这条消息。
主要特点(优点和缺点)
优点
- 解决了密钥分发问题: 不再需要事先安全地传递密钥,只需要公开分发公钥即可。
- 支持数字签名: 实现了身份验证和不可否认性,这是对称加密无法做到的。
缺点
- 速度极慢 (Very Slow): 非对称加密涉及复杂的数学运算(如大数分解、离散对数),其计算速度比对称加密慢几个数量级(通常是 100 到 1000 倍)。
- 不适合加密大量数据: 正因为速度慢,直接用非对称加密来加密大文件或视频是不切实际的。
混合加密:现实世界中的最佳实践
既然对称加密快,而非对称加密解决了密钥分发问题,那么将它们结合起来就是完美的方案。这就是混合加密系统 (Hybrid Encryption System),也是 HTTPS/TLS 等现代安全协议的工作核心。
流程如下:
- 非对称加密阶段:客户端使用服务器的公钥,加密一个随机生成的、临时的对称加密密钥,然后发送给服务器。
- 服务器使用自己的私钥解密,安全地获取了这个临时对称密钥。
- 对称加密阶段:现在,客户端和服务器都有了同一个共享的对称密钥。后续所有的通信都使用这个密钥,通过**速度飞快的对称加密算法(如 AES-GCM)**来进行。
这样,既利用了非对称加密的安全性来交换密钥,又利用了对称加密的高效率来传输数据。
非对称加密算法分类
RSA
算法介绍
RSA 是第一个,也是历史上最重要、应用最广泛的非对称加密算法。它的诞生彻底改变了密码学的面貌,使得在不安全的网络(如互联网)上进行安全通信和身份验证成为可能。它的名字来源于三位发明者的姓氏首字母:Rivest、Shamir 和 Adleman。
RSA 的安全性并非基于复杂的算法流程,而是依赖于一个非常简单的数论难题:大整数质因数分解 (Integer Factorization Problem)。
这个难题可以通俗地理解为:
- 乘法(简单): 给你两个非常大的素数(比如几百位数长),让你将它们相乘得到一个结果。这个计算用计算机可以瞬间完成。
- 分解(极其困难): 反过来,给你那个巨大的乘积结果,让你找出最初是哪两个素数相乘得到的。这个计算对于目前最强大的计算机来说,如果数字足够大,需要耗费数百年甚至更长的时间,在计算上被认为是不可行的。
在 RSA 中:
- 公钥 的生成与那个巨大的乘积有关。
- 私钥 的生成与那两个原始的巨大素数有关。
因此,全世界的人都知道你的公钥(那个大乘积),但只有你知道那两个“秘密”的素数因子,所以只有你能生成对应的私钥。这就是 RSA 安全性的根基。
JavaScript 实现
需先安装node-rsa
npm install node-rsa
// 1. 引入 node-rsa 库
const NodeRSA = require('node-rsa');// 2. 生成一个新的 2048 位的密钥对
// b: 密钥长度(位数),推荐 2048 或以上
const key = new NodeRSA({ b: 2048 });console.log("✅ 1. RSA 密钥对已生成。");// 导出 PEM 格式的公钥,以便分享
const publicKey = key.exportKey('public');
// console.log("公钥 (PEM 格式):\n", publicKey);// --- 示例 1: 加密与解密 ---
console.log("\n--- 示例 1: 加密与解密 ---");const messageToEncrypt = "This is a secret message!";
console.log("原始消息:", messageToEncrypt);// 3. 使用公钥加密
// 第二个参数 'base64' 指定了加密后输出的编码格式
const encrypted = key.encrypt(messageToEncrypt, 'base64');
console.log("加密后的 Base64:", encrypted);// 4. 使用私钥解密
// 第二个参数 'utf8' 指定了解密后输出的编码格式
const decrypted = key.decrypt(encrypted, 'utf8');
console.log("解密后的消息:", decrypted);
console.log("验证成功:", messageToEncrypt === decrypted);// --- 示例 2: 签名与验证 ---
console.log("\n--- 示例 2: 签名与验证 ---");const messageToSign = "This message is authentic.";
console.log("待签名的消息:", messageToSign);// 5. 使用私钥签名
// 第二个参数 'base64' 指定了签名输出的编码格式
const signature = key.sign(messageToSign, 'base64');
console.log("生成的签名 (Base64):", signature);// 6. 使用公钥验证
// 参数: 原始数据, 签名, 原始数据编码, 签名编码
const isVerified = key.verify(messageToSign, signature, 'utf8', 'base64');
console.log("签名是否有效:", isVerified);// 尝试验证一个被篡改的消息
const tamperedMessage = "This message is NOT authentic.";
const isTamperedVerified = key.verify(tamperedMessage, signature, 'utf8', 'base64');
console.log("篡改后消息的签名是否有效:", isTamperedVerified);
Python 实现
需先安装pycryptodome
pip install pycryptodome
import base64
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
from Crypto.Signature import pss
from Crypto.Hash import SHA256# 1. 生成一个新的 2048 位的 RSA 密钥对
key = RSA.generate(2048)
private_key = key
public_key = key.publickey()print("✅ 1. RSA 密钥对已生成。")# 为了方便展示,可以导出 PEM 格式的密钥
# public_pem = public_key.export_key().decode('utf-8')
# print("公钥 (PEM 格式):\n", public_pem)# --- 示例 1: 加密与解密 (使用 PKCS1_OAEP 填充) ---
print("\n--- 示例 1: 加密与解密 ---")message_to_encrypt = "This is a secret message!"
# 在 Python 中,加密操作处理的是字节 (bytes),所以需要编码
message_bytes = message_to_encrypt.encode('utf-8')
print(f"原始消息: '{message_to_encrypt}'")# 2. 使用公钥创建加密器对象
cipher_rsa = PKCS1_OAEP.new(public_key)# 3. 执行加密
encrypted = cipher_rsa.encrypt(message_bytes)
# 加密后的结果是字节,我们使用 Base64 编码以便显示
encrypted_b64 = base64.b64encode(encrypted).decode('utf-8')
print(f"加密后的 Base64: {encrypted_b64}")# 4. 使用私钥创建解密器对象
decipher_rsa = PKCS1_OAEP.new(private_key)# 5. 执行解密
decrypted_bytes = decipher_rsa.decrypt(base64.b64decode(encrypted_b64))
decrypted_message = decrypted_bytes.decode('utf-8')
print(f"解密后的消息: '{decrypted_message}'")
print(f"验证成功: {message_to_encrypt == decrypted_message}")# --- 示例 2: 签名与验证 (使用 PSS 签名方案) ---
print("\n--- 示例 2: 签名与验证 ---")message_to_sign = "This message is authentic."
message_hash = SHA256.new(message_to_sign.encode('utf-8'))
print(f"待签名的消息: '{message_to_sign}'")# 6. 使用私钥创建签名器对象
signer = pss.new(private_key)# 7. 对消息的哈希值进行签名
signature = signer.sign(message_hash)
signature_b64 = base64.b64encode(signature).decode('utf-8')
print(f"生成的签名 (Base64): {signature_b64}")# 8. 使用公钥创建验证器对象
verifier = pss.new(public_key)# 9. 验证签名
# pycryptodome 的推荐做法是使用 try/except 块来处理验证
try:verifier.verify(message_hash, signature)print("✅ 签名有效。")
except (ValueError, TypeError):print("❌ 签名无效!")# 尝试验证一个被篡改的消息
tampered_message_hash = SHA256.new(b"This message is NOT authentic.")
try:verifier.verify(tampered_message_hash, signature)print("✅ 篡改后消息的签名有效。 (这不应该发生!)")
except (ValueError, TypeError):print("❌ 篡改后消息的签名无效。 (符合预期)")