<p>The preimage used when signing messages with Bitcoin Core is a combination of the "Magic" phrase: <code>Bitcoin Signed Message:\n</code> and the Message you are signing. However there is one caveat, being you must have a <a href="https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer" rel="nofollow noreferrer">VarInt</a> prepended to both the "Magic" and the Message. The VarInt encodes the byte-length of the message (encoded in UTF-8). Since the "Magic" value never changes, its VarInt value will always be <code>0x18</code>.</p>

<p>For your example, the message is nothing, therefore your VarInt will be <code>0x00</code>. Your preimage and sighash becomes:</p>

<pre><code>Preimage: 0x18426974636f696e205369676e6564204d6573736167653a0a00
Sighash: SHA256(SHA256(preimage)) = 0x80e795d4a4caadd7047af389d9f7f220562feb6196032e2131e10563352c4bcc

    0x18       0x426974636f696e205369676e6564204d6573736167653a0a        0x00      (empty)
MAGIC_VARINT                       MAGIC                           MESSAGE_VARINT  MESSAGE
</code></pre>

CC BY-SA 4.0
2019-12-19T04:30:03.703Z
2019-12-19T04:30:03.703Z
Septem151
<p>I want to verify, in Java, a signature produced with bitcoin core's <code>signmessagewithprivkey</code> (equivalent to the <code>verifymessage</code>). I noticed a bitcoinj method similar to the bitcoin core's <code>verifymessage</code> - <a href="https://bitcoinj.github.io/javadoc/0.13/org/bitcoinj/core/ECKey.html#verify-org.bitcoinj.core.Sha256Hash-org.bitcoinj.core.ECKey.ECDSASignature-" rel="nofollow noreferrer">org.bitcoinj.core.ECKey.verify</a>. What follows is my implementation attempt and its test.</p>

<p>For testing I will generate a signature and check it in bitcoin core:</p>

<pre><code>&gt;bitcoin-cli signmessagewithprivkey $(bitcoin-cli dumpprivkey 1CwKH9PQPkFPjQagEv483FUM5ngk57L3Pp) ""
H2wp/+5N2+OQwP6a5GFRbt8S+EfML1Szx4uhWPfiO0e/QcY2rZQOkLOR+unknNl4NgDWBacRRXOLjr+m53V0xic=
&gt; bitcoin-cli verifymessage "1CwKH9PQPkFPjQagEv483FUM5ngk57L3Pp" "H2wp/+5N2+OQwP6a5GFRbt8S+EfML1Szx4uhWPfiO0e/QcY2rZQOkLOR+unknNl4NgDWBacRRXOLjr+m53V0xic=" ""
true
</code></pre>

<p>So, signing an empty message with the RPC using the address <code>1CwKH9PQPkFPjQagEv483FUM5ngk57L3Pp</code> (public key: <code>021c3be5fb7820c56d881ea2d02a906d87540ec8888bbe819b7abd2e39f6f6e512</code>), produces the signature <code>H2wp/+5N2+OQwP6a5GFRbt8S+EfML1Szx4uhWPfiO0e/QcY2rZQOkLOR+unknNl4NgDWBacRRXOLjr+m53V0xic=</code>.</p>

<p>My Java implementation attempt:</p>

<pre><code>import org.apache.commons.codec.binary.Hex;
import org.bitcoinj.core.ECKey;
import org.bitcoinj.core.Sha256Hash;
import java.math.BigInteger;
import java.util.Base64;

public class Notebook {

    public static BigInteger[] ParseSig(byte[] sigBytes, int sigOff) {

        BigInteger r = new BigInteger( 1 ,sigBytes, sigOff, 32);
        BigInteger s = new BigInteger( 1, sigBytes, sigOff + 32, 32);
        return new BigInteger[] { r, s };
    }

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

        // Signature
        String signatureString = "H2wp/+5N2+OQwP6a5GFRbt8S+EfML1Szx4uhWPfiO0e/QcY2rZQOkLOR+unknNl4NgDWBacRRXOLjr+m53V0xic=";
        byte[] signatureBytes = Base64.getDecoder().decode(signatureString);
        BigInteger[] signatureRandS = ParseSig(signatureBytes, 1);
        ECKey.ECDSASignature signature = new ECKey.ECDSASignature(signatureRandS[0], signatureRandS[1]);

        // Public key
        String pubKeyString = "021c3be5fb7820c56d881ea2d02a906d87540ec8888bbe819b7abd2e39f6f6e512";
        byte[] decodedPubKeyString = Hex.decodeHex(pubKeyString.toCharArray());
        ECKey publicKey = ECKey.fromPublicOnly(decodedPubKeyString);

        // Message
        String message = "Bitcoin Signed Message:\n";
        Sha256Hash messageHash = Sha256Hash.of(message.getBytes());
        Sha256Hash messageDoubleHash = Sha256Hash.of(messageHash.getBytes());

        // Test
        boolean result = publicKey.verify(messageDoubleHash, signature);
        System.out.println("Result: " + result);
    }
}
</code></pre>

<p>Notice that I used the salt <code>Bitcoin Signed Message:\n</code> before hashing the message - this is done by <code>signmessagewithprivkey</code> as shown <a href="https://github.com/bitcoin/bitcoin/blob/master/src/util/validation.cpp#L19" rel="nofollow noreferrer">here</a> and <a href="https://github.com/bitcoin/bitcoin/blob/master/src/rpc/misc.cpp#L331" rel="nofollow noreferrer">here</a>. The problem is the above implementation doesn't pass my test and I don't know why...</p>

<p><strong>EDIT:</strong></p>

<p>Thanks to Septem151 I was able to fix this. The code passes the test, if the message section is replaced with:</p>

<pre><code>        // Message
        String message = "Bitcoin Signed Message:\n";
        byte[] messageBytesEncoded = new byte[message.getBytes().length + 2];
        messageBytesEncoded[0] = 0x18;
        messageBytesEncoded[messageBytesEncoded.length - 1] = 0x00;
        for(int i=1; i &lt;= message.getBytes().length; i++) messageBytesEncoded[i] = message.getBytes()[i-1];

        Sha256Hash messageHash = Sha256Hash.of(messageBytesEncoded);
        Sha256Hash messageDoubleHash = Sha256Hash.of(messageHash.getBytes());
</code></pre>

CC BY-SA 4.0
2020-07-12T06:32:56.247Z
FYI you could use `"\030Bitcoin Signed Message:\n\0".getBytes()`
dave_thompson_085
<signature><bitcoinj><ecdsa><java>
How to verify a signed message by bitcoin core?
Pedro
# How to verify a signed message by bitcoin core?

(Asked by: username_0 on 2019-12-18)

I want to verify, in Java, a signature produced with bitcoin core's `signmessagewithprivkey` (equivalent to the `verifymessage`). I noticed a bitcoinj method similar to the bitcoin core's `verifymessage` - [org.bitcoinj.core.ECKey.verify](https://bitcoinj.github.io/javadoc/0.13/org/bitcoinj/core/ECKey.html#verify-org.bitcoinj.core.Sha256Hash-org.bitcoinj.core.ECKey.ECDSASignature-). What follows is my implementation attempt and its test.

For testing I will generate a signature and check it in bitcoin core:

```
>bitcoin-cli signmessagewithprivkey $(bitcoin-cli dumpprivkey 1CwKH9PQPkFPjQagEv483FUM5ngk57L3Pp) ""
H2wp/+5N2+OQwP6a5GFRbt8S+EfML1Szx4uhWPfiO0e/QcY2rZQOkLOR+unknNl4NgDWBacRRXOLjr+m53V0xic=
> bitcoin-cli verifymessage "1CwKH9PQPkFPjQagEv483FUM5ngk57L3Pp" "H2wp/+5N2+OQwP6a5GFRbt8S+EfML1Szx4uhWPfiO0e/QcY2rZQOkLOR+unknNl4NgDWBacRRXOLjr+m53V0xic=" ""
true

```

So, signing an empty message with the RPC using the address `1CwKH9PQPkFPjQagEv483FUM5ngk57L3Pp` (public key: `021c3be5fb7820c56d881ea2d02a906d87540ec8888bbe819b7abd2e39f6f6e512`), produces the signature `H2wp/+5N2+OQwP6a5GFRbt8S+EfML1Szx4uhWPfiO0e/QcY2rZQOkLOR+unknNl4NgDWBacRRXOLjr+m53V0xic=`.

My Java implementation attempt:

```
import org.apache.commons.codec.binary.Hex;
import org.bitcoinj.core.ECKey;
import org.bitcoinj.core.Sha256Hash;
import java.math.BigInteger;
import java.util.Base64;

public class Notebook {

    public static BigInteger[] ParseSig(byte[] sigBytes, int sigOff) {

        BigInteger r = new BigInteger( 1 ,sigBytes, sigOff, 32);
        BigInteger s = new BigInteger( 1, sigBytes, sigOff + 32, 32);
        return new BigInteger[] { r, s };
    }

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

        // Signature
        String signatureString = "H2wp/+5N2+OQwP6a5GFRbt8S+EfML1Szx4uhWPfiO0e/QcY2rZQOkLOR+unknNl4NgDWBacRRXOLjr+m53V0xic=";
        byte[] signatureBytes = Base64.getDecoder().decode(signatureString);
        BigInteger[] signatureRandS = ParseSig(signatureBytes, 1);
        ECKey.ECDSASignature signature = new ECKey.ECDSASignature(signatureRandS[0], signatureRandS[1]);

        // Public key
        String pubKeyString = "021c3be5fb7820c56d881ea2d02a906d87540ec8888bbe819b7abd2e39f6f6e512";
        byte[] decodedPubKeyString = Hex.decodeHex(pubKeyString.toCharArray());
        ECKey publicKey = ECKey.fromPublicOnly(decodedPubKeyString);

        // Message
        String message = "Bitcoin Signed Message:\n";
        Sha256Hash messageHash = Sha256Hash.of(message.getBytes());
        Sha256Hash messageDoubleHash = Sha256Hash.of(messageHash.getBytes());

        // Test
        boolean result = publicKey.verify(messageDoubleHash, signature);
        System.out.println("Result: " + result);
    }
}

```

Notice that I used the salt `Bitcoin Signed Message:\n` before hashing the message - this is done by `signmessagewithprivkey` as shown [here](https://github.com/bitcoin/bitcoin/blob/master/src/util/validation.cpp#L19) and [here](https://github.com/bitcoin/bitcoin/blob/master/src/rpc/misc.cpp#L331). The problem is the above implementation doesn't pass my test and I don't know why...

**EDIT:**

Thanks to username_2 I was able to fix this. The code passes the test, if the message section is replaced with:

```
        // Message
        String message = "Bitcoin Signed Message:\n";
        byte[] messageBytesEncoded = new byte[message.getBytes().length + 2];
        messageBytesEncoded[0] = 0x18;
        messageBytesEncoded[messageBytesEncoded.length - 1] = 0x00;
        for(int i=1; i <= message.getBytes().length; i++) messageBytesEncoded[i] = message.getBytes()[i-1];

        Sha256Hash messageHash = Sha256Hash.of(messageBytesEncoded);
        Sha256Hash messageDoubleHash = Sha256Hash.of(messageHash.getBytes());

```

**Tags:** signature, bitcoinj, ecdsa, java

> **Comments:**
> **username_1**: FYI you could use `"\030Bitcoin Signed Message:\n\0".getBytes()`  

---

## 1 answers

### Answer by username_2 on 2019-12-19. Score: 3 | Accepted answer

The preimage used when signing messages with Bitcoin Core is a combination of the "Magic" phrase: `Bitcoin Signed Message:\n` and the Message you are signing. However there is one caveat, being you must have a [VarInt](https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer) prepended to both the "Magic" and the Message. The VarInt encodes the byte-length of the message (encoded in UTF-8). Since the "Magic" value never changes, its VarInt value will always be `0x18`.

For your example, the message is nothing, therefore your VarInt will be `0x00`. Your preimage and sighash becomes:

```
Preimage: 0x18426974636f696e205369676e6564204d6573736167653a0a00
Sighash: SHA256(SHA256(preimage)) = 0x80e795d4a4caadd7047af389d9f7f220562feb6196032e2131e10563352c4bcc

    0x18       0x426974636f696e205369676e6564204d6573736167653a0a        0x00      (empty)
MAGIC_VARINT                       MAGIC                           MESSAGE_VARINT  MESSAGE

```


---

