Transaction-Verification
驗證比特幣交易
繼偉大的文章 [需要逐步兌換原始交易範例,我想出了下面的程式碼,但驗證失敗。
// pubkeyBytes, len: 65 // 042daa93315eebbe2cb9b5c3505df4c6fb6caca8b756786098567550d4820c09db988fe9997d049d687292f815ccd6e7fb5c1b1a91137999818d17c73d0f80aef9 // data, len: 32 // 30f10a6468b7d98257af63fb40dfcf2cefe991346fd37c67cf7b51ff8d4404d3 // signatureBytes, len: 71 // 30450220587ce0cf0252e2db3a7c3c91b355aa8f3385e128227cd8727c5f7777877ad772022100edc508b7c14891ed15ab38c687019d7ebaf5c12908cf21a83e8ae57e8c47e95c ECPublicKey publicKey = (ECPublicKey)KeyBuilder.buildKey(KeyBuilder.TYPE_EC_FP_PUBLIC, KeyBuilder.LENGTH_EC_FP_256, false);; Secp256k1.setCommonCurveParameters(publicKey); publicKey.setW(pubkeyBytes, (short) 0, (short) pubkeyBytes.length); Signature eccSign = Signature.getInstance(Signature.ALG_ECDSA_SHA_256, false); eccSign.init(publicKey, Signature.MODE_VERIFY); boolean verified = eccSign.verify(data, (short)0, (short) data.length, signatureBytes, (short)0, (short)signatureBytes.length ); Assert.assertTrue(verified);
斷言最終失敗。
被困了好幾個小時。想不通。非常感謝任何幫助。謝謝。
static void test_sig ( ) { const MyByteArray pubkey ( QByteArray::fromHex ( "042daa93315eebbe2cb9b5c3505df4c6fb6caca8b756786098567550d4820c09db988fe9997d049d687292f815ccd6e7fb5c1b1a91137999818d17c73d0f80aef9" ) ); const MyByteArray signat ( QByteArray::fromHex ( "30450220587ce0cf0252e2db3a7c3c91b355aa8f3385e128227cd8727c5f7777877ad772022100edc508b7c14891ed15ab38c687019d7ebaf5c12908cf21a83e8ae57e8c47e95c" ) ); const MyKey32 digest ( QByteArray::fromHex ( "30f10a6468b7d98257af63fb40dfcf2cefe991346fd37c67cf7b51ff8d4404d3" ) ); qDebug ( ) << ( digest.verify ( pubkey, signat ) ? "passed" : "failed" ); }
我的結果是::通過
是
signature
正確的digest
並且public key
您的驗證碼可能有問題,解碼十六進制值和/或摘要中的字節順序?
通常問題是使用的簽名驗證算法。比特幣使用雙 SHA-256 作為 ECDSA 的散列算法,因此 Z 值是未簽名交易的雙 SHA-256 散列。查看您正在使用的簽名雜湊算法:
簽名算法 ALG_ECDSA_SHA_256 生成 32 字節 SHA-256 摘要並使用 ECDSA 和 ECKey 參數中定義的曲線對摘要進行簽名/驗證
您已經獲取了 Z 值,但隨後僅使用 1 輪 SHA-256 再次對其進行散列。您可以嘗試傳入只有 1 個 SHA-256 散列的原像,然後讓 Signature 對象處理第二輪,但我對使用標準 Java 安全庫的這種方法不太滿意。為了在 Java 中完成比特幣交易簽名/驗證,您需要 (1) 實現自己的 ECDSA 簽名/驗證,或者 (2) 使用諸如 BitcoinJ 之類的庫。以下是我在我的 ECPubKey 類中我自己的項目中使用的程式碼(請注意,我正在使用大量其他自定義程式碼,您會從中看到方法呼叫,但未包括在此處,但您將了解要點):
/** * @param preimage the Preimage to verify * @param signature An ECDSA Signature (R/S Value pair) * @return whether this Public Key was used to generate the signature for the given preimage */ public boolean verify(String preimage, ECSignature signature) { String sigHash = HashUtil.doubleSha256(preimage); BigInteger z = new BigInteger(1, ByteUtil.hex2bytes(sigHash)); BigInteger r = signature.getR(); BigInteger s = signature.getS(); BigInteger sInv = s.modInverse(CurveParams.n); BigInteger u1 = z.multiply(sInv).mod(CurveParams.n); BigInteger u2 = r.multiply(sInv).mod(CurveParams.n); ECPoint u1Point = ScalarMultiply.scalmult(CurveParams.G, u1); ECPoint u2Point = ScalarMultiply.scalmult(this.getPoint(), u2); ECPoint sigPoint = ScalarMultiply.addPoint(u1Point, u2Point); return r.compareTo(sigPoint.getAffineX().mod(CurveParams.n)) == 0; }
該方法基本上直接從ECDSA 維基百科頁面複製並翻譯成 Java。