5

创建签名消息后,我不确定如何使用生成的签名来使用 publicKey 验证消息。

我的用例是,我想使用 Solana 钱包登录 API 服务器,其模式如下:

  1. GET message: String (from API server)
  2. sign message with privateKey
  3. POST signature (to API server)
  4. verify signature with stored publicKey

我尝试使用 nodeJScrypto.verify来解码 API 端的签名消息,但是我对缓冲区和椭圆曲线的深入挖掘有点超出了我的深度:

// Front-end code
const toHexString = (buffer: Buffer) =>
  buffer.reduce((str, byte) => str + byte.toString(16).padStart(2, "0"), "");

const data = new TextEncoder().encode('message to verify');
const signed = await wallet.sign(data, "hex");
await setLogin({ // sends API post call to backend
  variables: {
    publicAddress: walletPublicKey,
    signature: toHexString(signed.signature),
  },
});

// Current WIP for backend code
const ALGORITHM = "ed25519";
const fromHexString = (hexString) =>
  new Uint8Array(hexString.match(/.{1,2}/g).map((byte) => parseInt(byte, 16)));
const signature = fromHexString(args.signature);
const nonceUint8 = new TextEncoder().encode('message to verify');
const verified = crypto.verify(
  ALGORITHM,
  nonceUint8,
  `-----BEGIN PUBLIC KEY-----\n${user.publicAddress}\n-----END PUBLIC KEY-----`,
  signature
);
console.log("isVerified: ", verified);

我很确定我会以错误的方式解决这个问题,并且一定有一个明显的方法我错过了。

随着空间的成熟,我希望验证函数或库似乎会消耗const signed = await wallet.sign(data, "hex");

就像是:

import { VerifyMessage } from '@solana/web3.js';

const verified = VerifyMessage(message, publicKey, signature, 'hex');

但是经过 3 天的努力,我开始达到我的极限,我的大脑正在衰竭。非常感谢任何帮助或方向

4

4 回答 4

6

我建议留在 solana-labs trail 并使用 tweetnacl

spl-token-wallet (sollet.io) 使用 nacl.sign.detached(message, this.account.secretKey)

https://github.com/project-serum/spl-token-wallet/blob/9c9f1d48a589218ffe0f54b7d2f3fb29d84f7b78/src/utils/walletProvider/localStorage.js#L65-L67

在另一端,验证完成 nacl.sign.detached.verify

在@solana/web3.js https://github.com/solana-labs/solana/blob/master/web3.js/src/transaction.ts#L560

在你的后端使用nacl.sign.detached.verify,你应该很好。我还建议避免任何数据格式操作,我不确定您要做什么,但如果您确实验证每个步骤是否正确。

于 2021-07-09T00:12:16.737 回答
6

由出色的 Project Serum discord 开发人员提供的输入解决。高级解决方案是使用在 sol-wallet-adapter 存储库中也使用的库,即tweetnaclbs58

const signatureUint8 = base58.decode(args.signature);
const nonceUint8 = new TextEncoder().encode(user?.nonce);
const pubKeyUint8 = base58.decode(user?.publicAddress);

nacl.sign.detached.verify(nonceUint8, signatureUint8, pubKeyUint8)
// true
于 2021-07-09T01:06:51.717 回答
1

对于 iOS,solana.request 会导致错误。使用 solana.signMessage 和 base58 对签名进行编码。

var _signature = ''; 
try {
  signedMessage = await window.solana.request({
    method: "signMessage",
    params: {
      message: encodedMessage
    },
  }); 
  _signature = signedMessage.signature; 
} catch (e) { 
  try {
    signedMessage = await window.solana.signMessage(encodedMessage); 
    _signature = base58.encode(signedMessage.signature); 
  } catch (e1) {
    alert(e1.message);
  }
}
// 
try {
  signIn('credentials',
    {
      publicKey: signedMessage.publicKey,
      signature: _signature,
      callbackUrl: `${window.location.origin}/`
    }
  )
} catch (e) {
  alert(e.message);
}
于 2022-02-07T08:48:16.570 回答
0

我需要将 Uint8Array 转换为字符串并将其转换回 Uint8Array 以进行 HTTP 通信。我发现toLocaleStringUint8Array 的方法在这种情况下很有帮助。它将逗号分隔的整数输出为字符串。

const signedMessage = await window.solana.signMessage(encodedMessage, "utf8");
const signature = signedMessage.signature.toLocaleString();

在此处输入图像描述

然后您可以使用以下代码将其转换回 Uint8Array。

const signatureUint8 = new Uint8Array(signature.split(",").map(Number));

编辑

上面的解决方案在桌面上运行,但是当我在 Phantom 钱包 iOS 浏览器中尝试我的代码时,它给出了一个错误。我猜 toLocaleString 方法在该浏览器中不可用。我找到了一个更可靠的解决方案来将 Uint8Array 转换为逗号分隔的字符串

Array.apply([], signedMessage.signature).join(",")
于 2022-02-04T18:44:00.150 回答