您现在的位置是:首页 > 文章详情

为什么我对签名消息的签名验证在PHP代码中未工作?

日期:2018-07-19点击:432

我正在尝试验证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/

另外推荐几个很受欢迎全网稀缺的互动教程:

  • php以太坊,主要是介绍使用php进行智能合约开发交互,进行账号创建、交易、转账、代币开发以及过滤器和事件等内容。
  • python以太坊,主要是针对python围绕web3.py进行区块链以太坊应用开发的讲解。
  • web3j,主要是针对java和android程序员围绕web3j库进行区块链以太坊开发的讲解。
  • 以太坊开发,主要是介绍使用node.js、mongodb、区块链、ipfs实现去中心化电商DApp实战,适合进阶。
  • 以太坊教程,主要介绍智能合约与dapp应用开发,适合入门。
原文链接:https://yq.aliyun.com/articles/617027
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章