<p>Your signatures don't verify because in JCE you are using algorithm <code>SHA256withECDSA</code> which hashes the supplied data (re-hashes, since you are supplying a hash) before doing the kG.x,kinv(h+rd) computation, or the similar verification computation, whereas the spongy/bouncy ECDSASigner used in bitcoinj only does the latter, assuming the input is already the correct hash. If you use JCE <code>NONEwithECDSA</code> instead, it will verify okay.</p>

<p>Note however the correct hash for bitcoin transaction signing is double SHA256, not the single SHA256 you coded. Although the data you are SHA256'ing isn't a bitcoin transaction at all, so the fact you are hashing it wrong is kind of moot.</p>

<p>Finally, even with these fixed, the signatures will still be <em>different</em>. JCE using the SunEC provider uses random K as per the standard (X9.62 and FIPS 186-2), whereas BC LWAPI can and the bitcoinj code you copied does use 'synthetic' K per RFC6979. Both are valid, but the signature values are totally different. (The BC provider does have <em>some</em> 'ECDDSA' (D for deterministic) algorithms using this K calculation, but only with hashing, and anyway if you have BC provider you also have BC LWAPI.) </p>

<p>In addition, as you seem to have detected, bitcoin uses 'low S' signatures; neither JCE <em>nor</em> BC LWAPI does that for you, so in either case you must add it (as bitcoinj does in <code>toCanonicalised</code>).</p>

CC BY-SA 4.0
2019-06-28T20:58:45.163Z
2019-06-28T20:58:45.163Z
dave_thompson_085
<p>I need to sign Bitcoin transactions using JCE primitives(no bitcoinj), but my signatures are not considered valid by bitcoinj.</p>

<p>I tried to simulate this process. I created a random hash and signed it using both JCE and bitcoinj. Signatures are not equal.</p>

<p>Here is the code</p>

<pre><code>import org.bitcoinj.core.ECKey;
import org.bitcoinj.core.Sha256Hash;
import org.spongycastle.crypto.digests.SHA256Digest;
import org.spongycastle.crypto.params.ECPrivateKeyParameters;
import org.spongycastle.crypto.signers.ECDSASigner;
import org.spongycastle.crypto.signers.HMacDSAKCalculator;
import sun.security.ec.ECPrivateKeyImpl;
import sun.security.ec.ECPublicKeyImpl;

import java.math.BigInteger;
import java.security.*;
import java.security.spec.ECGenParameterSpec;

public class ECDSABitcoin {

    private static final String SIGN_ALGORITHM = "SHA256withECDSA";

    public static void main(String[] args) throws Exception {

        KeyPairGenerator keyPairGenerator = createGenerator();
        final KeyPair keyPair = keyPairGenerator.generateKeyPair();
        ECPrivateKeyImpl privateKey = (ECPrivateKeyImpl) keyPair.getPrivate();
        ECPublicKeyImpl publicKey = (ECPublicKeyImpl) keyPair.getPublic();
        try {
            Sha256Hash hashOut = Sha256Hash.wrap(toSha256("abc".getBytes()));
            byte[] signatureBytes = sign(hashOut.getBytes(), keyPair);

            ECKey.ECDSASignature mySignature = ECKey.ECDSASignature.decodeFromDER(signatureBytes).toCanonicalised();
            ECKey.ECDSASignature bitcoinSignature = sign(privateKey.getS(), hashOut.getBytes()).toCanonicalised();
            System.out.println("My signature s " + mySignature.s + " r " + mySignature.r + " canonical " + mySignature.isCanonical());
            System.out.println("Verify my " + verify(keyPair, hashOut.getBytes(), signatureBytes));
            System.out.println("Bitcoinj signature s " + bitcoinSignature.s + " r " + bitcoinSignature.r + " canonical " + bitcoinSignature.isCanonical());
            System.out.println("Verify Bitcoinj " + verify(keyPair, hashOut.getBytes(), bitcoinSignature.encodeToDER()));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static boolean verify(KeyPair keyPair, byte[] message, byte[] signatureBytes) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
        final Signature verifySignature = Signature.getInstance(SIGN_ALGORITHM);
        verifySignature.initVerify(keyPair.getPublic());
        verifySignature.update(message);
        return verifySignature.verify(signatureBytes);
    }

    private static byte[] sign(byte[] message, KeyPair keyPair) throws InvalidKeyException, NoSuchAlgorithmException, SignatureException {
        final Signature signature = Signature.getInstance(SIGN_ALGORITHM);
        signature.initSign(keyPair.getPrivate());
        signature.update(message);
        return signature.sign();
    }

    private static KeyPairGenerator createGenerator() throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
        final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC");
        ECGenParameterSpec ecParam = new ECGenParameterSpec("secp256k1");
        keyPairGenerator.initialize(ecParam);
        return keyPairGenerator;
    }

    private static ECKey.ECDSASignature sign(BigInteger privateKeyForSigning, byte[] data) {
        ECDSASigner signer = new ECDSASigner(new HMacDSAKCalculator(new SHA256Digest()));
        ECPrivateKeyParameters privKey = new ECPrivateKeyParameters(privateKeyForSigning, ECKey.CURVE);
        signer.init(true, privKey);
        BigInteger[] components = signer.generateSignature(data);
        return new ECKey.ECDSASignature(components[0], components[1]).toCanonicalised();
    }

    private static byte[] toSha256(byte[] message) throws NoSuchAlgorithmException {
        MessageDigest crypt = MessageDigest.getInstance("SHA-256");
        crypt.reset();
        crypt.update(message);
        return crypt.digest();
    }
}
</code></pre>

<p>The result is:</p>

<pre><code>My signature s 45669553786690215047884329722902825758089042579493437816717142987836102849876 r 14778973653615637448416336446742229796258878351047437829727432860950944374049 canonical true
Verify my true
Bitcoinj signature s 24278043061766196831119988370534304503511938256487950554838614741011144316017 r 26413727078831382349368962255251267289169651926313668837949728205557969096319 canonical true
Verify Bitcoinj false
</code></pre>

<p>As you see, signatures are completely different, even though I use the same private key. What is wrong with my code? I just don't get it.</p>

<bitcoinj><ecdsa><java>
How to sign Bitcoin transactions using JCE?
Tony
# How to sign Bitcoin transactions using JCE?

(Asked by: username_0 on 2019-06-27)

I need to sign Bitcoin transactions using JCE primitives(no bitcoinj), but my signatures are not considered valid by bitcoinj.
I tried to simulate this process. I created a random hash and signed it using both JCE and bitcoinj. Signatures are not equal.
Here is the code
import org.bitcoinj.core.ECKey;
import org.bitcoinj.core.Sha256Hash;
import org.spongycastle.crypto.digests.SHA256Digest;
import org.spongycastle.crypto.params.ECPrivateKeyParameters;
import org.spongycastle.crypto.signers.ECDSASigner;
import org.spongycastle.crypto.signers.HMacDSAKCalculator;
import sun.security.ec.ECPrivateKeyImpl;
import sun.security.ec.ECPublicKeyImpl;

import java.math.BigInteger;
import java.security.*;
import java.security.spec.ECGenParameterSpec;

public class ECDSABitcoin {

    private static final String SIGN_ALGORITHM = "SHA256withECDSA";

    public static void main(String[] args) throws Exception {

        KeyPairGenerator keyPairGenerator = createGenerator();
        final KeyPair keyPair = keyPairGenerator.generateKeyPair();
        ECPrivateKeyImpl privateKey = (ECPrivateKeyImpl) keyPair.getPrivate();
        ECPublicKeyImpl publicKey = (ECPublicKeyImpl) keyPair.getPublic();
        try {
            Sha256Hash hashOut = Sha256Hash.wrap(toSha256("abc".getBytes()));
            byte[] signatureBytes = sign(hashOut.getBytes(), keyPair);

            ECKey.ECDSASignature mySignature = ECKey.ECDSASignature.decodeFromDER(signatureBytes).toCanonicalised();
            ECKey.ECDSASignature bitcoinSignature = sign(privateKey.getS(), hashOut.getBytes()).toCanonicalised();
            System.out.println("My signature s " + mySignature.s + " r " + mySignature.r + " canonical " + mySignature.isCanonical());
            System.out.println("Verify my " + verify(keyPair, hashOut.getBytes(), signatureBytes));
            System.out.println("Bitcoinj signature s " + bitcoinSignature.s + " r " + bitcoinSignature.r + " canonical " + bitcoinSignature.isCanonical());
            System.out.println("Verify Bitcoinj " + verify(keyPair, hashOut.getBytes(), bitcoinSignature.encodeToDER()));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static boolean verify(KeyPair keyPair, byte[] message, byte[] signatureBytes) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
        final Signature verifySignature = Signature.getInstance(SIGN_ALGORITHM);
        verifySignature.initVerify(keyPair.getPublic());
        verifySignature.update(message);
        return verifySignature.verify(signatureBytes);
    }

    private static byte[] sign(byte[] message, KeyPair keyPair) throws InvalidKeyException, NoSuchAlgorithmException, SignatureException {
        final Signature signature = Signature.getInstance(SIGN_ALGORITHM);
        signature.initSign(keyPair.getPrivate());
        signature.update(message);
        return signature.sign();
    }

    private static KeyPairGenerator createGenerator() throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
        final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC");
        ECGenParameterSpec ecParam = new ECGenParameterSpec("secp256k1");
        keyPairGenerator.initialize(ecParam);
        return keyPairGenerator;
    }

    private static ECKey.ECDSASignature sign(BigInteger privateKeyForSigning, byte[] data) {
        ECDSASigner signer = new ECDSASigner(new HMacDSAKCalculator(new SHA256Digest()));
        ECPrivateKeyParameters privKey = new ECPrivateKeyParameters(privateKeyForSigning, ECKey.CURVE);
        signer.init(true, privKey);
        BigInteger[] components = signer.generateSignature(data);
        return new ECKey.ECDSASignature(components[0], components[1]).toCanonicalised();
    }

    private static byte[] toSha256(byte[] message) throws NoSuchAlgorithmException {
        MessageDigest crypt = MessageDigest.getInstance("SHA-256");
        crypt.reset();
        crypt.update(message);
        return crypt.digest();
    }
}

The result is:
My signature s 45669553786690215047884329722902825758089042579493437816717142987836102849876 r 14778973653615637448416336446742229796258878351047437829727432860950944374049 canonical true
Verify my true
Bitcoinj signature s 24278043061766196831119988370534304503511938256487950554838614741011144316017 r 26413727078831382349368962255251267289169651926313668837949728205557969096319 canonical true
Verify Bitcoinj false

As you see, signatures are completely different, even though I use the same private key. What is wrong with my code? I just don't get it.

**Tags:** bitcoinj, ecdsa, java


---

## 1 answers

### Answer by username_1 on 2019-06-28. Score: 1 | Accepted answer

Your signatures don't verify because in JCE you are using algorithm `SHA256withECDSA` which hashes the supplied data (re-hashes, since you are supplying a hash) before doing the kG.x,kinv(h+rd) computation, or the similar verification computation, whereas the spongy/bouncy ECDSASigner used in bitcoinj only does the latter, assuming the input is already the correct hash. If you use JCE `NONEwithECDSA` instead, it will verify okay.

Note however the correct hash for bitcoin transaction signing is double SHA256, not the single SHA256 you coded. Although the data you are SHA256'ing isn't a bitcoin transaction at all, so the fact you are hashing it wrong is kind of moot.

Finally, even with these fixed, the signatures will still be *different*. JCE using the SunEC provider uses random K as per the standard (X9.62 and FIPS 186-2), whereas BC LWAPI can and the bitcoinj code you copied does use 'synthetic' K per RFC6979. Both are valid, but the signature values are totally different. (The BC provider does have *some* 'ECDDSA' (D for deterministic) algorithms using this K calculation, but only with hashing, and anyway if you have BC provider you also have BC LWAPI.)

In addition, as you seem to have detected, bitcoin uses 'low S' signatures; neither JCE *nor* BC LWAPI does that for you, so in either case you must add it (as bitcoinj does in `toCanonicalised`).


---

