0

Following the great post [Redeeming a raw transaction step by step example required, I came up with code below but the verification failed.

// 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);

Assertion failed at the end.

Have been stucked for hours. Cannot figure it out. Any help is highly appreciated. Thanks.

Septem151
  • 667
  • 4
  • 12

2 Answers2

1

Usually the issue is the Signature Verification algorithm used. Bitcoin uses double SHA-256 as the hashing algorithm for ECDSA, thus the Z value is the Double SHA-256 hash of the unsigned transaction. Looking at the Signature Hash Algorithm you're using:

Signature algorithm ALG_ECDSA_SHA_256 generates a 32-byte SHA-256 digest and signs/verifies the digest using ECDSA with the curve defined in the ECKey parameters

You are taking in the Z value already but then hashing it again with only 1 round of SHA-256. You could try to pass in a preimage of only 1 SHA-256 hash then let the Signature object handle the second round, but I do not have much luck with this method using standard Java Security libraries. In order to accomplish Bitcoin Transaction signing/verification in Java, you will need to either (1) implement your own ECDSA signing/verification, or (2) use a library such as BitcoinJ. The following is the code I use in my own project(s) within my ECPubKey class (note that I am using a ton of other custom code that you will see method calls from, yet are not included here, but you will get the gist):

/**
 * @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;
}

The methodology was essentially copied directly from the ECDSA Wikipedia Page and translated into Java.

Septem151
  • 667
  • 4
  • 12
0
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" );
}

my result is:: passed

the signature is correct for digest and public key

may be something is wrong in your verification code with decoding hex values and/or byte order in digest?

amaclin
  • 6,760
  • 1
  • 21
  • 32