PHP(Laravel)后端如何验证Elrond签名并获取对应钱包地址
PHP(Laravel) 后端 Elrond 钱包签名验证实现方案
依赖安装
直接通过Composer安装验签所需的两个稳定依赖:
composer require tuupola/ed25519 composer require bitwasp/bech32
前后端传参约定
请求payload必须携带以下字段,避免验签逻辑歧义:
address:用户提交的Elrond钱包地址,固定为erd1开头格式signature:钱包生成的签名,十六进制编码字符串message:签名对应的原始明文,必须携带一次性随机nonce、过期时间等防重放字段,比如固定格式为Login to [你的dApp名称] nonce:xxx expire:xxxnonce:后端提前下发的一次性随机校验串
注意:禁止使用无任何上下文的固定消息做签名,否则攻击者截获合法签名后可无限重放请求。
核心验签逻辑
直接在控制器中实现即可,核心逻辑包含参数校验、nonce有效性校验、公钥提取、消息补全、ED25519验签、地址二次比对6个环节:
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use Tuupola\Ed25519; use BitWasp\Bech32\Bech32; class WalletAuthController extends Controller { const ELROND_SIGN_FIXED_PREFIX = "Elrond Signed Message:\n"; const ELROND_ADDRESS_PREFIX = "erd"; public function loginBySignature(Request $request) { // 基础参数格式校验 $params = $request->validate([ 'address' => 'required|string|starts_with:erd1', 'signature' => 'required|string|hex:size:128', // 64字节签名转十六进制固定128位 'message' => 'required|string', 'nonce' => 'required|string|size:16' ]); // 一次性nonce校验:提前在下发nonce时绑定客户端IP、设置2分钟过期,校验通过立刻删除 if (cache()->pull("elrond_login_nonce:{$params['nonce']}") !== $request->ip()) { return response()->json(['error' => '非法请求,nonce已失效'], 403); } try { // 1. 从前端传入地址解码出公钥原始字节 list($prefix, $groupedPubKey) = Bech32::decode($params['address']); if ($prefix !== self::ELROND_ADDRESS_PREFIX) { throw new \Exception("地址前缀非法"); } // bech32解码结果为5位分组,转换为8位原始公钥字节 $rawPubKey = Bech32::convertBits($groupedPubKey, 5, 8, false); if (strlen($rawPubKey) !== 32) { // ED25519公钥固定32字节 throw new \Exception("公钥长度非法"); } // 2. 构造Elrond钱包实际签名的完整消息:固定前缀+消息长度+原始消息 // 所有官方Elrond钱包签名时都会自动拼接该前缀,漏加会导致验签100%失败 $fullSignMessage = self::ELROND_SIGN_FIXED_PREFIX . strlen($params['message']) . $params['message']; // 3. 十六进制签名转原始二进制 $rawSignature = hex2bin($params['signature']); if (strlen($rawSignature) !== 64) { // ED25519签名固定64字节 throw new \Exception("签名长度非法"); } // 4. ED25519签名校验 $ed25519 = new Ed25519(); if (!$ed25519->verify($fullSignMessage, $rawSignature, $rawPubKey)) { throw new \Exception("签名不匹配"); } // 5. 用公钥重新生成钱包地址,和前端传入地址做二次比对,彻底防篡改 $regeneratedGroupKey = Bech32::convertBits($rawPubKey, 8, 5); $realAddress = Bech32::encode(self::ELROND_ADDRESS_PREFIX, $regeneratedGroupKey); if ($realAddress !== $params['address']) { throw new \Exception("提交地址与签名公钥不匹配"); } // 所有校验通过,$realAddress 就是真实合法的钱包地址,后续写自己的登录逻辑即可 return response()->json([ 'code' => 0, 'msg' => '登录成功', 'wallet_address' => $realAddress ]); } catch (\Exception $e) { return response()->json(['error' => '校验失败:' . $e->getMessage()], 403); } } }
高频踩坑点
- 漏加Elrond固定签名前缀:官方钱包签名时会自动拼接
Elrond Signed Message:\n+消息长度的前缀,直接拿前端传的原始消息验签必然失败 - 未做nonce一次性校验:合法签名被截获后可被攻击者重复使用,必须给每个登录预请求生成唯一nonce,绑定客户端信息、设置短过期时间、用后即删
- 跳过地址二次比对:若bech32解码逻辑存在兼容问题,可能出现公钥和传入地址不匹配的情况,必须用公钥重新生成地址做全等比对
- 未做长度校验:非法长度的签名、公钥参数会触发底层依赖报错,提前做格式拦截可降低异常率
内容的提问来源于stack exchange,提问作者GPaul
相关产品推荐
相关产品推荐

