为什么我对签名消息的签名验证在PHP代码中未工作?
我正在尝试验证PHP中的签名消息。
我不希望与JSON-RPC或任何外部服务有接口,我知道这两个服务都是可以成功地验证了我提供的示例签名消息。
另外,我很清楚message prefix
和消息长度问题。
这个问题似乎源于我正在使用的Signature
类或对R和S值的GMP转换,在签名本身中起的作用。
在我的代码中使用的唯一外部库是https://github.com/0xbb/php-sha3/blob/master/src/Sha3.php,它需要对第334行进行修改,将0x06
更改为0x01
,用于keccak
兼容,这是以太坊所使用的。这里的改变应该说是一个正确的改变,因为在用web3库hash同一条消息时,当哈希原始消息时,将其保持为0x01
会产生不同的结果。
下面是我的代码。如果有人能告诉我哪里出了问题,我将不胜感激。
<?php use bb\Sha3\Sha3; require_once('./Sha3.php'); $message = 'This is an example of a signed message.'; $signerAddress = '0xd4e01f608982ff53022e8c3ff43e145a192a9c4a'; $signedMessage = '0x6a65ed07a44715169177223ce508a2257f8167db452df0b2e37966b39350a61940e370616b3a0ea0f20adfa4661a7db10eeb583ca5a58ec8468e726eff4131a11c'; $signedMessageStrip = '6a65ed07a44715169177223ce508a2257f8167db452df0b2e37966b39350a61940e370616b3a0ea0f20adfa4661a7db10eeb583ca5a58ec8468e726eff4131a11c'; $prefix = "\x19Ethereum Signed Message:\n".strlen($message); $stringToSign = $prefix.$message; //\x19Ethereum Signed Message:\n39This is an example of a signed message. $messageHex = Sha3::hash($stringToSign, 256); //this matches web3.sha() output for the given message and prefix. $messageGmp = gmp_init("0x".$messageHex); $r = substr($signedMessageStrip, 0,64); $s = substr($signedMessageStrip, 64,64); $v = substr($signedMessageStrip, 128,2); $vChecksum = hexdec($v) - 27; if($vChecksum !== 0 && $vChecksum !== 1) { echo "Invalid checksum.\n"; exit; } $rGmp = gmp_init("0x".$r); $sGmp = gmp_init("0x".$s); $publicKey = Signature::recoverPublicKey($rGmp, $sGmp, $messageGmp, $vChecksum); //the below line is where things are going wrong. The output hash of Sha3::hash($publicKey['x'].$publicKey['y'], 256) is not correct, according to stepping through similar processes using the web3 library, which generates different results, despite an earlier check that publicKey *is* correct. I cannot figure out what's going wrong. $recovered = "0x".substr(Sha3::hash($publicKey['x'].$publicKey['y'], 256),24)."\n"; //convert to public address format //$recovered = 0xf2517bd73c56d6d5a5409c4a1ee29c8f2d5438ff if (strtolower($recovered) == strtolower($signerAddress)) { echo "Address recovered successfully.\n"; } else { echo "Address NOT recovered successfully.\n"; } ?> <?php class SECp256k1 { public $a; public $b; public $p; public $n; public $G; public function __construct(){ $this->a = gmp_init('0', 10); $this->b = gmp_init('7', 10); $this->p = gmp_init('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F', 16); $this->n = gmp_init('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141', 16); $this->G = array('x' => gmp_init('55066263022277343669578718895168534326250603453777594175500187360389116729240'), 'y' => gmp_init('32670510020758816978083085130507043184471273380659243275938904335757337482424')); } } class Signature { public static function recoverPublicKey($R, $S, $hash, $recoveryFlags){ $secp256k1 = new SECp256k1(); $a = $secp256k1->a; $b = $secp256k1->b; $G = $secp256k1->G; $n = $secp256k1->n; $p = $secp256k1->p; $isYEven = ($recoveryFlags & 1) != 0; $isSecondKey = ($recoveryFlags & 2) != 0; // PointMathGMP::mulPoint wants HEX String $e = gmp_strval($hash, 16); $s = gmp_strval($S, 16); // Precalculate (p + 1) / 4 where p is the field order // $p_over_four is GMP static $p_over_four; // XXX just assuming only one curve/prime will be used if (!$p_over_four) { $p_over_four = gmp_div(gmp_add($p, 1), 4); } // 1.1 Compute x // $x is GMP if (!$isSecondKey) { $x = $R; } else { $x = gmp_add($R, $n); } // 1.3 Convert x to point // $alpha is GMP $alpha = gmp_mod(gmp_add(gmp_add(gmp_pow($x, 3), gmp_mul($a, $x)), $b), $p); // $beta is DEC String (INT) $beta = gmp_strval(gmp_powm($alpha, $p_over_four, $p)); // If beta is even, but y isn't or vice versa, then convert it, // otherwise we're done and y == beta. if (PointMathGMP::isEvenNumber($beta) == $isYEven) { // gmp_sub function will convert the DEC String "$beta" into a GMP // $y is a GMP $y = gmp_sub($p, $beta); } else { // $y is a GMP $y = gmp_init($beta); } // 1.4 Check that nR is at infinity (implicitly done in construtor) -- Not reallly // $Rpt is Array(GMP, GMP) $Rpt = array('x' => $x, 'y' => $y); // 1.6.1 Compute a candidate public key Q = r^-1 (sR - eG) // $rInv is a HEX String $rInv = gmp_strval(gmp_invert($R, $n), 16); // $eGNeg is Array (GMP, GMP) $eGNeg = PointMathGMP::negatePoint(PointMathGMP::mulPoint($e, $G, $a, $b, $p)); $sR = PointMathGMP::mulPoint($s, $Rpt, $a, $b, $p); $sR_plus_eGNeg = PointMathGMP::addPoints($sR, $eGNeg, $a, $p); // $Q is Array (GMP, GMP) $Q = PointMathGMP::mulPoint($rInv, $sR_plus_eGNeg, $a, $b, $p); // Q is the derrived public key // $pubkey is Array (HEX String, HEX String) // Ensure it's always 64 HEX Charaters $pubKey['x'] = str_pad(gmp_strval($Q['x'], 16), 64, 0, STR_PAD_LEFT); $pubKey['y'] = str_pad(gmp_strval($Q['y'], 16), 64, 0, STR_PAD_LEFT); return $pubKey; } } class PointMathGMP { /*** * Computes the result of a point addition and returns the resulting point as an Array. * * @param Array $pt * @return Array Point * @throws \Exception */ public static function doublePoint(Array $pt, $a, $p) { $gcd = gmp_strval(gmp_gcd(gmp_mod(gmp_mul(gmp_init(2, 10), $pt['y']), $p),$p)); if($gcd != '1') { throw new \Exception('This library doesn\'t yet supports point at infinity. See https://github.com/BitcoinPHP/BitcoinECDSA.php/issues/9'); } // SLOPE = (3 * ptX^2 + a )/( 2*ptY ) // Equals (3 * ptX^2 + a ) * ( 2*ptY )^-1 $slope = gmp_mod( gmp_mul( gmp_invert( gmp_mod( gmp_mul( gmp_init(2, 10), $pt['y'] ), $p ), $p ), gmp_add( gmp_mul( gmp_init(3, 10), gmp_pow($pt['x'], 2) ), $a ) ), $p ); // nPtX = slope^2 - 2 * ptX // Equals slope^2 - ptX - ptX $nPt = array(); $nPt['x'] = gmp_mod( gmp_sub( gmp_sub( gmp_pow($slope, 2), $pt['x'] ), $pt['x'] ), $p ); // nPtY = slope * (ptX - nPtx) - ptY $nPt['y'] = gmp_mod( gmp_sub( gmp_mul( $slope, gmp_sub( $pt['x'], $nPt['x'] ) ), $pt['y'] ), $p ); return $nPt; } /*** * Computes the result of a point addition and returns the resulting point as an Array. * * @param Array $pt1 * @param Array $pt2 * @return Array Point * @throws \Exception */ public static function addPoints(Array $pt1, Array $pt2, $a, $p) { if(gmp_cmp($pt1['x'], $pt2['x']) == 0 && gmp_cmp($pt1['y'], $pt2['y']) == 0) //if identical { return self::doublePoint($pt1, $a, $p); } $gcd = gmp_strval(gmp_gcd(gmp_sub($pt1['x'], $pt2['x']), $p)); if($gcd != '1') { throw new \Exception('This library doesn\'t yet support points at infinity. See https://github.com/BitcoinPHP/BitcoinECDSA.php/issues/9'); } // SLOPE = (pt1Y - pt2Y)/( pt1X - pt2X ) // Equals (pt1Y - pt2Y) * ( pt1X - pt2X )^-1 $slope = gmp_mod( gmp_mul( gmp_sub( $pt1['y'], $pt2['y'] ), gmp_invert( gmp_sub( $pt1['x'], $pt2['x'] ), $p ) ), $p ); // nPtX = slope^2 - ptX1 - ptX2 $nPt = array(); $nPt['x'] = gmp_mod( gmp_sub( gmp_sub( gmp_pow($slope, 2), $pt1['x'] ), $pt2['x'] ), $p ); // nPtX = slope * (ptX1 - nPtX) - ptY1 $nPt['y'] = gmp_mod( gmp_sub( gmp_mul( $slope, gmp_sub( $pt1['x'], $nPt['x'] ) ), $pt1['y'] ), $p ); return $nPt; } /*** * Computes the result of a point multiplication and returns the resulting point as an Array. * * @param String Hex $k * @param Array $pG (GMP, GMP) * @param $base (INT) * @throws \Exception * @return Array Point (GMP, GMP) */ public static function mulPoint($k, Array $pG, $a, $b, $p, $base = null) { //in order to calculate k*G if($base == 16 || $base == null || is_resource($base)) $k = gmp_init($k, 16); if($base == 10) $k = gmp_init($k, 10); $kBin = gmp_strval($k, 2); $lastPoint = $pG; for($i = 1; $i < strlen($kBin); $i++) { if(substr($kBin, $i, 1) == 1 ) { $dPt = self::doublePoint($lastPoint, $a, $p); $lastPoint = self::addPoints($dPt, $pG, $a, $p); } else { $lastPoint = self::doublePoint($lastPoint, $a, $p); } } if(!self::validatePoint(gmp_strval($lastPoint['x'], 16), gmp_strval($lastPoint['y'], 16), $a, $b, $p)){ throw new \Exception('The resulting point is not on the curve.'); } return $lastPoint; } /*** * Returns true if the point is on the curve and false if it isn't. * * @param $x * @param $y * @return bool */ public static function validatePoint($x, $y, $a, $b, $p) { $x = gmp_init($x, 16); $y2 = gmp_mod( gmp_add( gmp_add( gmp_powm($x, gmp_init(3, 10), $p), gmp_mul($a, $x) ), $b ), $p ); $y = gmp_mod(gmp_pow(gmp_init($y, 16), 2), $p); if(gmp_cmp($y2, $y) == 0) return true; else return false; } /*** * Returns Negated Point (Y). * * @param $point Array(GMP, GMP) * @return Array(GMP, GMP) */ public static function negatePoint($point) { return array('x' => $point['x'], 'y' => gmp_neg($point['y'])); } // Checks is the given number (DEC String) is even public static function isEvenNumber($number) { return (((int)$number[strlen($number)-1]) & 1) == 0; } } ?>
问题解答
将问题与所产生的公钥的hash分开,事实证明,必须将公钥的字节,而不是十六进制hash本身传递给keccak
哈希算法。
$recovered = "0x".substr(Sha3::hash(hex2bin($publicKey['x'].$publicKey['y']), 256),24)
原文《以太坊常见问题和错误》中的:
http://cw.hubwiz.com/card/c/ethereum-FAQ/1/1/20/
另外推荐几个很受欢迎全网稀缺的互动教程:
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
RDS SQL Server死锁(Deadlock)系列之一使用DBCC捕获死锁
问题引入 在日常运维阿里云RDS SQL Server产品过程中,经常会被客户问道:“应用程序被死锁报错啦?影响很大,到底是哪个进程导致了死锁发生的啊?怎么解决啊?怎么办呀?”。从客户一连串的问题中,我们深刻体会到了死锁问题的紧迫性和影响之大。授人予鱼而不如授人予渔,RDS SQL Server死锁系列文章就是为了帮助客人彻底解决死锁问题为初衷而诞生的。本篇文章是系列文章的开篇,主要是讨论如何使用DBCC来捕获死锁信息,内容包括: DBCC捕获死锁 死锁测试 死锁分析 解决方法 DBCC捕获死锁 DBCC捕获死锁是利用了SQL Server死锁自动监测机制(默认每5秒运行一次)的返回信息,来将死锁信息记录到数据库日志记录中,我们可以事后从错误日中来查看这些有用的死锁信息,包括: 死锁的牺牲进程 死锁发生时的进程信息 死锁发生时争抢的资源 其实,DBCC捕获死锁信息的方法本身非常简单,只需要使用DBCC命令打开两个跟踪标记(1222和1204)即可。方法如下: USE master GO DBCC TRACEON(1222,-1) GO --also write like ...
- 下一篇
php如何使用JSON-RPC查找以太坊中的最新块哈希hash值?
我用PHP中开发了一个以太坊块资源管理器,并成功地与我的服务器Geth节点建立了JSON-RPC通信。然而,当我试图找出最后一个区块时,我陷入了困境。我在https://ethereum.gitbooks.io/frontier-guide/content/rpc.html中读到了很不错的内容,但是我没有任何方法去查询到以太坊节点的哪一个是最后一个块。一旦我得到了块哈希,我就可以有接口检索完整的信息块,但我不知道如何找到最新的块哈希。 API接口中eth_blockNumber应该返回最新的块号,但是如何得到哈希? 问题解答 user@Kumquat:~$ curl -X POST --data '{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["latest", false],"id":1}' localhost:8545 {"jsonrpc":"2.0","id":1,"result":{"difficulty":"0x31962a3fc82b", "extraData":"0x4477617266506f6f6...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
-
Docker使用Oracle官方镜像安装(12C,18C,19C)
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS8编译安装MySQL8.0.19
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- MySQL8.0.19开启GTID主从同步CentOS8
- CentOS7,8上快速安装Gitea,搭建Git服务器
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
推荐阅读
最新文章
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- CentOS6,CentOS7官方镜像安装Oracle11G
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- CentOS7设置SWAP分区,小内存服务器的救世主
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- 2048小游戏-低调大师作品
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- MySQL8.0.19开启GTID主从同步CentOS8
- CentOS7,8上快速安装Gitea,搭建Git服务器