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

Deno中Node.js crypto.sign的等价实现及代码迁移方案

迁移Google Access Token生成代码到Deno的解决方案

核心差异处理

Deno基于Web标准API,替代Node.js的Buffer和crypto.sign需要以下针对性调整:

  1. 替换Buffer.from
    Deno中使用TextEncoder将字符串转为Uint8Array,完全等效Node.js的Buffer.from功能。

  2. 替换crypto.sign
    改用Deno内置的Web Crypto API(crypto.subtle)实现RS256签名,需先将PEM格式私钥导入为CryptoKey对象。

  3. 调整Base64URL编码
    基于Web API的btoa实现,处理字符替换与末尾等号去除逻辑。


完整Deno代码实现

// 读取环境变量
const clientEmail = Deno.env.get("CLIENT_EMAIL")!;
const privateKeyPem = Deno.env.get("PRIVATE_KEY")!;

// Base64URL编码实现
function base64urlEncode(data: Uint8Array | string): string {
  let base64: string;
  if (typeof data === "string") {
    base64 = btoa(unescape(encodeURIComponent(data)));
  } else {
    base64 = btoa(String.fromCharCode(...data));
  }
  return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
}

// 导入PEM格式私钥为CryptoKey
async function importPrivateKey(pem: string): Promise<CryptoKey> {
  // 去除PEM头部尾部并解码Base64
  const pemHeader = "-----BEGIN PRIVATE KEY-----";
  const pemFooter = "-----END PRIVATE KEY-----";
  const pemContents = pem.replace(pemHeader, "").replace(pemFooter, "").replace(/\s/g, "");
  const binaryDer = Uint8Array.from(atob(pemContents), c => c.charCodeAt(0));

  return crypto.subtle.importKey(
    "pkcs8",
    binaryDer,
    {
      name: "RSASSA-PKCS1-v1_5",
      hash: "SHA-256"
    },
    false,
    ["sign"]
  );
}

// 创建JWT
async function createJWT(privateKey: CryptoKey): Promise<string> {
  const iat = Math.floor(Date.now() / 1000);
  const exp = iat + 3600; // 有效期1小时

  const header = JSON.stringify({ alg: "RS256", typ: "JWT" });
  const claimSet = JSON.stringify({
    iss: clientEmail,
    scope: "https://www.googleapis.com/auth/firebase.messaging",
    aud: "https://oauth2.googleapis.com/token",
    exp,
    iat
  });

  // 构造未签名的JWT部分
  const unsignedJWT = `${base64urlEncode(header)}.${base64urlEncode(claimSet)}`;
  // 将字符串转为Uint8Array用于签名
  const unsignedBytes = new TextEncoder().encode(unsignedJWT);
  // 生成签名
  const signature = await crypto.subtle.sign(
    "RSASSA-PKCS1-v1_5",
    privateKey,
    unsignedBytes
  );
  // 签名转Base64URL
  const signatureBase64Url = base64urlEncode(new Uint8Array(signature));

  return `${unsignedJWT}.${signatureBase64Url}`;
}

// 获取Google Access Token
async function getAccessToken(): Promise<string> {
  const privateKey = await importPrivateKey(privateKeyPem);
  const jwt = await createJWT(privateKey);

  const response = await fetch("https://oauth2.googleapis.com/token", {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body: new URLSearchParams({
      grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
      assertion: jwt
    })
  });

  const data = await response.json();
  return data.access_token;
}

// 使用示例
// getAccessToken().then(token => console.log("Access Token:", token));

关键说明

  • 环境变量:确保CLIENT_EMAIL和PRIVATE_KEY已正确设置,私钥保持原PEM格式即可,无需额外转换。
  • 异步处理:Web Crypto API的importKey和sign均为异步操作,因此相关函数需使用async/await语法。
  • 编码兼容:处理字符串时先转URI编码,避免Unicode字符导致的Base64编码异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 21:30:13