使用Ed25519 KeyPair进行JWS(JSON Web Signature)的签名和验证

5
我希望使用在客户端设备上生成的Ed25519私钥签署JWS(JSON Web Signature),并将此签名发送到后端进行公钥验证。 为了熟悉该过程,我想在Node.js中尝试签署和验证JWS。
我的私钥和公钥都已经生成,并且可用于base58格式。以下是我使用Ed25519私钥签署JWT的当前尝试:
const { SignJWT } = require("jose/jwt/sign");
const bs50 = require("bs58");

async function main() {
  const publicBase58 = "A77GCUCZ7FAuXVMKtwwXyFhMa158XsaoGKHYNnJ1q3pv";
  const privateKeyBase58 = "BE1VM7rTRJReLsTLLG4JMNX5ozcp7qpmMuRht9zB1UjU";

  const publicKeyBuffer = bs50.decode(publicBase58);
  const privateKeyBuffer = bs50.decode(privateKeyBase58);

  const publicKey = new Uint8Array(publicKeyBuffer);
  const privateKey = new Uint8Array(privateKeyBuffer);

  const jwt = await new SignJWT({
    subject: "uuid",
  })
    .setProtectedHeader({ alg: "EdDSA" })
    .setExpirationTime("2h")
    .sign(privateKey);

  console.log(jwt);
}

错误: TypeError: 键必须是KeyObject或CryptoKey类型之一。接收到一个Uint8Array实例

当尝试使用sign()函数时,由于我的私钥是Uint8Array类型,因此会出现上述错误,唯一接受的类型为KeyObjectCryptoKey,但我不知道如何将我的Uint8Arrays转换为KeyObjectsCryptoKeys

我从这个答案得到了一些代码片段。


https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/importKey - fredrik
我该如何导入 subtle 模块。我已经尝试过 const { subtle } = require('crypto').webcrypto; - JonasLevin
1个回答

4

你需要将密钥以 Node.js 可识别的格式进行转换。使用 KeyObject.create*Key API 进行转换。对于 Ed25519 密钥,只要 Node.js 版本号 >= 16.0.0 并且使用以下格式:

  • 公钥需要以 SPKI 的 PEM/DER 格式
  • 私钥需要以 PKCS8 的 PEM/DER 格式
  • 公钥和私钥都可以使用 JWK 格式

下面是一个使用 DER 格式的代码片段。

import { SignJWT, jwtVerify } from "jose"
import bs58 from "bs58"
import { createPrivateKey, createPublicKey } from "crypto"

(async function main() {
  const publicBase58 = "A77GCUCZ7FAuXVMKtwwXyFhMa158XsaoGKHYNnJ1q3pv";
  const privateKeyBase58 = "BE1VM7rTRJReLsTLLG4JMNX5ozcp7qpmMuRht9zB1UjU";

  let publicKey = bs58.decode(publicBase58);
  let privateKey = bs58.decode(privateKeyBase58);

  publicKey = createPublicKey({
    key: Buffer.concat([Buffer.from("302a300506032b6570032100", "hex"), publicKey]),
    format: "der",
    type: "spki",
  });

  privateKey = createPrivateKey({
    key: Buffer.concat([
      Buffer.from("302e020100300506032b657004220420", "hex"),
      privateKey,
    ]),
    format: "der",
    type: "pkcs8",
  })

  const jwt = await new SignJWT({
    subject: "uuid",
  })
    .setProtectedHeader({ alg: "EdDSA" })
    .setExpirationTime("2h")
    .sign(privateKey);

  console.log(await jwtVerify(jwt, publicKey))
})()

这里有一个使用JWK的例子。

import { SignJWT, jwtVerify } from "jose"
import bs58 from "bs58"
import { createPrivateKey, createPublicKey } from "crypto"

(async function main() {
  const publicBase58 = "A77GCUCZ7FAuXVMKtwwXyFhMa158XsaoGKHYNnJ1q3pv";
  const privateKeyBase58 = "BE1VM7rTRJReLsTLLG4JMNX5ozcp7qpmMuRht9zB1UjU";

  let publicKey = bs58.decode(publicBase58);
  let privateKey = bs58.decode(privateKeyBase58);

  publicKey = createPublicKey({
    key: {
      kty: "OKP",
      crv: "Ed25519",
      x: publicKey.toString("base64url")
    },
    format: "jwk"
  });

  privateKey = createPrivateKey({
    key: {
      kty: "OKP",
      crv: "Ed25519",
      x: publicKey.toString("base64url"),
      d: privateKey.toString("base64url"),
    },
    format: "jwk"
  })

  const jwt = await new SignJWT({
    subject: "uuid",
  })
    .setProtectedHeader({ alg: "EdDSA" })
    .setExpirationTime("2h")
    .sign(privateKey);

  console.log(await jwtVerify(jwt, publicKey))
})()

谢谢,"der"格式在我的情况下有效。有没有一种方法可以在不先验证密钥的情况下读取已签名密钥的有效载荷?我想将用户ID存储在有效载荷中,并使用该ID可以获取存储在DLT上的公钥。 - JonasLevin
在验证令牌之前处理数据本质上是危险的。因此,该库不提供这样的手段。 - user9775882
话虽如此,只需要编写 JSON.parse(Buffer.from(jwt.split('.')[1], 'base64')) 即可简单实现。 - user9775882

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接