You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于Javascript/NodeJS与PHP实现端到端加密(E2EE)?

基础端到端加密(E2EE)实现方案:JS/Node.js + PHP

我来帮你梳理这个场景的实现思路和代码示例,刚好之前做过类似需求,咱们一步步来:

核心逻辑

端到端加密的关键是密钥完全由客户端掌控——服务器全程只跟密文打交道,根本碰不到你的明文数据和密钥,这才是真正的E2EE。整体流程:

  • 客户端生成加密密钥,仅存在本地
  • 客户端加密明文后,将密文+初始化向量(IV)发送给服务器存储
  • 客户端从服务器拉取密文+IV后,用本地密钥解密得到明文

1. 客户端加密(JavaScript)

咱们用浏览器原生的Web Crypto API实现,比第三方库更安全可靠,不用额外引入依赖。

生成并存储密钥(仅客户端执行)

// 生成AES-GCM算法的密钥(可导出,方便存在localStorage)
async function generateKey() {
  const key = await window.crypto.subtle.generateKey(
    { name: "AES-GCM", length: 256 },
    true, // 是否允许导出密钥
    ["encrypt", "decrypt"] // 密钥用途
  );
  // 把密钥导出成JWK格式,存在localStorage
  const exportedKey = await window.crypto.subtle.exportKey("jwk", key);
  localStorage.setItem("e2ee-key", JSON.stringify(exportedKey));
  return key;
}

结合表单的加密+发送示例

对应你提到的表单场景,完整代码如下:

<form id="message-form">
  <input type="text" name="message" placeholder="输入要加密的内容">
  <button type="submit">加密发送</button>
</form>

<script>
document.getElementById('message-form').addEventListener('submit', async (e) => {
  e.preventDefault();
  const plaintext = document.querySelector('input[name="message"]').value;
  
  // 获取密钥:本地有就复用,没有就生成
  let key;
  const storedKey = localStorage.getItem("e2ee-key");
  if (storedKey) {
    key = await window.crypto.subtle.importKey(
      "jwk",
      JSON.parse(storedKey),
      { name: "AES-GCM", length: 256 },
      true,
      ["encrypt", "decrypt"]
    );
  } else {
    key = await generateKey();
  }
  
  // 生成随机IV(AES-GCM必须用随机IV,不能重复,否则会泄露明文)
  const iv = window.crypto.getRandomValues(new Uint8Array(12));
  
  // 把明文转成ArrayBuffer供加密使用
  const encoder = new TextEncoder();
  const data = encoder.encode(plaintext);
  
  // 执行加密
  const encryptedData = await window.crypto.subtle.encrypt(
    { name: "AES-GCM", iv: iv },
    key,
    data
  );
  
  // 把密文和IV转成Base64,方便网络传输
  const ciphertextBase64 = btoa(String.fromCharCode(...new Uint8Array(encryptedData)));
  const ivBase64 = btoa(String.fromCharCode(...iv));
  
  // 发送到服务器
  await fetch('/save-message.php', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      ciphertext: ciphertextBase64,
      iv: ivBase64
    })
  });
  
  document.querySelector('input[name="message"]').value = '';
  alert('消息已加密发送!');
});
</script>

2. 服务器存储(PHP)

服务器只需要做「存储工具人」,接收密文和IV直接存数据库就行,完全不用碰加密逻辑:

<?php
// save-message.php
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *'); // 开发环境用,生产环境请指定具体域名

// 接收客户端的JSON数据
$data = json_decode(file_get_contents('php://input'), true);
if (!$data || !isset($data['ciphertext'], $data['iv'])) {
  echo json_encode(['status' => 'error', 'message' => '参数缺失']);
  exit;
}

// 连接数据库(用PDO防止SQL注入)
try {
  $pdo = new PDO('mysql:host=localhost;dbname=your_db;charset=utf8mb4', 'db_user', 'db_pwd');
  $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  
  // 插入密文和IV到数据库
  $stmt = $pdo->prepare('INSERT INTO messages (ciphertext, iv, created_at) VALUES (:ciphertext, :iv, NOW())');
  $stmt->execute([
    ':ciphertext' => $data['ciphertext'],
    ':iv' => $data['iv']
  ]);
  
  echo json_encode(['status' => 'success']);
} catch (PDOException $e) {
  echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
}
?>

3. 客户端解密(JavaScript)

从服务器拉取密文后,用本地密钥解密:

async function fetchAndDecryptMessages() {
  // 从服务器获取加密后的消息列表
  const response = await fetch('/get-messages.php');
  const messages = await response.json();
  
  // 获取本地存储的密钥
  const storedKey = localStorage.getItem("e2ee-key");
  if (!storedKey) {
    alert('未找到密钥,无法解密!');
    return;
  }
  const key = await window.crypto.subtle.importKey(
    "jwk",
    JSON.parse(storedKey),
    { name: "AES-GCM", length: 256 },
    true,
    ["encrypt", "decrypt"]
  );
  
  // 逐个解密消息
  const decryptedMessages = await Promise.all(messages.map(async (msg) => {
    // 把Base64转成ArrayBuffer
    const ciphertext = Uint8Array.from(atob(msg.ciphertext), c => c.charCodeAt(0));
    const iv = Uint8Array.from(atob(msg.iv), c => c.charCodeAt(0));
    
    // 执行解密
    const decryptedData = await window.crypto.subtle.decrypt(
      { name: "AES-GCM", iv: iv },
      key,
      ciphertext
    );
    
    // 转成明文
    const decoder = new TextDecoder();
    return decoder.decode(decryptedData);
  }));
  
  // 把解密后的消息渲染到页面(示例:打印到控制台)
  console.log('解密后的消息:', decryptedMessages);
}

// 调用函数获取并解密
fetchAndDecryptMessages();

对应的PHP查询接口get-messages.php:

<?php
// get-messages.php
header('Content-Type: application/json');
header('Access-Control-Allow-Origin: *');

try {
  $pdo = new PDO('mysql:host=localhost;dbname=your_db;charset=utf8mb4', 'db_user', 'db_pwd');
  $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  
  // 查询所有加密消息
  $stmt = $pdo->query('SELECT ciphertext, iv FROM messages ORDER BY created_at DESC');
  $messages = $stmt->fetchAll(PDO::FETCH_ASSOC);
  
  echo json_encode($messages);
} catch (PDOException $e) {
  echo json_encode(['status' => 'error', 'message' => $e->getMessage()]);
}
?>

关键注意事项

  • 密钥安全:示例用localStorage存密钥,适合基础场景;如果是敏感数据,建议结合浏览器的安全存储机制,绝对不能把密钥发送到服务器!
  • 加密算法:一定要用AES-GCM(带认证的加密),它能同时保证机密性和完整性,防止密文被篡改;绝对不要用ECB模式(完全不安全)。
  • IV的必要性:IV必须随机生成,每次加密都要用新的IV,重复IV会导致明文泄露。
  • 传输安全:即使是E2EE,数据传输也要用HTTPS,防止密文被中间人拦截或篡改(AES-GCM能检测篡改,但HTTPS能提前规避风险)。
  • Node.js适配:如果是Node.js客户端(比如Electron),可以用Node.js内置的crypto模块替代Web Crypto API,逻辑完全一致,仅API细节略有不同。

内容的提问来源于stack exchange,提问作者nsylke

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 08:53:28