接口数据加密传输RSA加AES

wen java案例 2

本文目录导读:

接口数据加密传输RSA加AES

  1. 核心逻辑
  2. 准备 RSA 密钥对 (服务端)
  3. 客户端加密 (以浏览器/Node.js为例)
  4. 服务端解密 (以 Node.js 为例)
  5. 安全要点 (必须注意)

这是一个非常经典的混合加密方案(Hybrid Encryption),在单次接口通信中,RSA(非对称加密)用于安全传输AES的密钥,AES(对称加密)用于高效加密实际数据。

下面我会提供一个标准的、高安全性的实现流程,包含 Node.js 和浏览器端的示例代码。

核心逻辑

  1. 客户端:生成一个随机的 AES 密钥。
  2. 客户端:用这个 AES 密钥加密请求体的 JSON 数据(得到 encryptedData)。
  3. 客户端:用服务端的 RSA 公钥 加密这个 AES 密钥(得到 encryptedKey)。
  4. 客户端:将 encryptedKeyencryptedData 一起发送给服务端。
  5. 服务端:用自己的 RSA 私钥解密 encryptedKey,得到 AES 密钥。
  6. 服务端:用这个 AES 密钥解密 encryptedData,得到原始请求数据。

准备 RSA 密钥对 (服务端)

服务端需要生成 2048 位或更长的 RSA 密钥对。

# 生成私钥
openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048
# 从私钥导出公钥
openssl rsa -pubout -in private_key.pem -out public_key.pem
  • private_key.pem: 放在服务端,绝对保密。
  • public_key.pem: 分发给客户端(前端/移动端)。

客户端加密 (以浏览器/Node.js为例)

我们使用 Web Crypto API (浏览器) 或 Node.js crypto 模块。

依赖库安装 (Node.js 环境)

npm install node-forge  # 或者使用内置 crypto 模块,但 forje 在浏览器端更容易移植

客户端加密代码 (JavaScript)

// 假设你已经从服务器获取了 RSA 公钥字符串
const rsaPublicKeyPem = `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr...
-----END PUBLIC KEY-----`;
// 1. 生成 AES 密钥 (随机 256位)
function generateAESKey() {
    // 使用 Web Crypto API (浏览器环境)
    if (window.crypto && window.crypto.subtle) {
        return crypto.subtle.generateKey({
            name: "AES-CBC",
            length: 256
        }, true, ["encrypt", "decrypt"]);
    }
    // 回退到 Node.js (这里不演示,因为浏览器更常见)
    throw new Error("Crypto API not supported");
}
// 2. 使用 AES 加密数据
async function encryptDataWithAES(aesKey, plaintext) {
    const encoder = new TextEncoder();
    const data = encoder.encode(JSON.stringify(plaintext)); // 先转JSON
    const iv = crypto.getRandomValues(new Uint8Array(16)); // 初始化向量,非常重要
    const encrypted = await crypto.subtle.encrypt({
        name: "AES-CBC",
        iv: iv
    }, aesKey, data);
    // 返回 IV 和密文的 base64 拼接 (或者分开传)
    return {
        iv: arrayBufferToBase64(iv),
        data: arrayBufferToBase64(encrypted)
    };
}
// 3. 使用 RSA 公钥加密 AES 密钥
async function encryptAESKeyWithRSA(rsaPublicKeyPem, aesKey) {
    // 1. 导入公钥
    const publicKey = await importRSAPublicKey(rsaPublicKeyPem);
    // 2. 导出 AES 密钥的原始字节
    const rawAesKey = await crypto.subtle.exportKey("raw", aesKey);
    // 3. 使用 RSA-OAEP 加密 (推荐)
    const encryptedKey = await crypto.subtle.encrypt({
        name: "RSA-OAEP"
    }, publicKey, rawAesKey);
    return arrayBufferToBase64(encryptedKey);
}
// 辅助函数: 导入 RSA 公钥
async function importRSAPublicKey(pem) {
    const pemHeader = "-----BEGIN PUBLIC KEY-----";
    const pemFooter = "-----END PUBLIC KEY-----";
    const pemContents = pem.substring(pemHeader.length, pem.length - pemFooter.length);
    const binaryDer = base64ToArrayBuffer(pemContents);
    return await crypto.subtle.importKey(
        "spki",
        binaryDer,
        {
            name: "RSA-OAEP",
            hash: "SHA-256"
        },
        false,
        ["encrypt"]
    );
}
// 辅助函数: ArrayBuffer 转 Base64
function arrayBufferToBase64(buffer) {
    let binary = '';
    const bytes = new Uint8Array(buffer);
    for (let i = 0; i < bytes.byteLength; i++) {
        binary += String.fromCharCode(bytes[i]);
    }
    return btoa(binary);
}
function base64ToArrayBuffer(base64) {
    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;
}
// 主流程: 加密请求
async function encryptRequest(payload) {
    const aesKey = await generateAESKey();
    const encryptedPayload = await encryptDataWithAES(aesKey, payload);
    const encryptedKey = await encryptAESKeyWithRSA(rsaPublicKeyPem, aesKey);
    // 发送给服务端的数据结构
    return {
        key: encryptedKey,  // RSA 加密后的 AES 密钥
        iv: encryptedPayload.iv,
        data: encryptedPayload.data
    };
}
// 使用示例
const requestData = { userId: 123, amount: 100.00, timestamp: Date.now() };
encryptRequest(requestData).then(encryptedBody => {
    // 发送到 /api/secure-transfer
    fetch('/api/secure-transfer', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(encryptedBody)
    });
});

服务端解密 (以 Node.js 为例)

使用 Node.js 内置的 crypto 模块。

const crypto = require('crypto');
const fs = require('fs');
// 加载私钥
const privateKey = fs.readFileSync('./private_key.pem', 'utf8');
function decryptRequest(encryptedBody) {
    const { key: encryptedKeyB64, iv: ivB64, data: encryptedDataB64 } = encryptedBody;
    // 1. 用 RSA 私钥解密 AES 密钥
    const decryptedKey = crypto.privateDecrypt(
        {
            key: privateKey,
            padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
            oaepHash: 'sha256'
        },
        Buffer.from(encryptedKeyB64, 'base64')
    );
    // 2. 创建 AES 解密器
    const decipher = crypto.createDecipheriv(
        'aes-256-cbc',
        decryptedKey,
        Buffer.from(ivB64, 'base64')
    );
    // 3. 解密数据
    let decrypted = decipher.update(Buffer.from(encryptedDataB64, 'base64'));
    decrypted = Buffer.concat([decrypted, decipher.final()]);
    // 4. 解析 JSON
    return JSON.parse(decrypted.toString('utf8'));
}
// 示例 Express 路由
app.post('/api/secure-transfer', (req, res) => {
    try {
        const originalData = decryptRequest(req.body);
        // 正常处理业务逻辑
        console.log('解密后的数据:', originalData);
        res.json({ status: 'success', data: originalData });
    } catch (error) {
        console.error('解密失败:', error.message);
        res.status(400).json({ status: 'error', message: '解密失败' });
    }
});

安全要点 (必须注意)

问题 解决方案
中间人攻击 (MITM) 必须使用 HTTPS,RSA 只加密了 AES 密钥,但公钥分发过程若被篡改,攻击者可以替换自己的公钥。HTTPS 是基础
RSA 密钥长度 建议使用 2048位4096位,4096更安全但加密速度慢。
AES 加密模式 推荐 AES-256-GCM (比 CBC 更安全,自带认证标签防止篡改),示例使用了 CBC,如果换成 GCM 需额外处理 authTag
初始化向量 (IV) 必须 随机生成,且每次请求不同。
填充方式 (Padding) RSA 使用 OAEP (推荐) 而非 PKCS#1 v1.5,AES 使用标准填充 (PKCS7)。
重放攻击 (Replay) 在明文数据中加入 timestampnonce (一次性随机数),服务端校验时间差和是否已使用过 nonce。
密钥存储 私钥使用环境变量或密钥管理服务 (KMS),不要硬编码在代码里。

这套混合加密方案是目前最实用的接口加密方式之一:

  • RSA 解决密钥传输的安全问题(无法被窃听)。
  • AES 解决大数据的加密效率问题。
  • HTTPS 解决身份验证和公钥完整性验证问题。

这三个共同构成了一个相对可靠的加密传输通道。

抱歉,评论功能暂时关闭!