Signatures

Cryptographic signatures in Bitcoin are done using an elliptic curve. Specifically the secp256k1 variant of standard/known curves. Consequently all signatures employ the Elliptic Curve Diffie-Hellman Signature Algorithm (ECDSA) with SHA256 hashes.

In general, one can sign the hash of an arbitrary piece of data using ECDSA. In bitcoin the signatures are typically generated by signing a sha256 hash value. This hash value is derived from the data in a transaction. You can refer to the section on Signature Schemes from the Developer Guide to learn more about exactly which parts of a transaction are included in the hash value calculation.

The classes related to ECDSA signatures are :

class description
TransactionSigner Helper class that allows us to generate signatures over a Transaction based on specific SIGHASH flags
TransactionSignature A transaction-specific wrapper around ECKey.ECDSASignature
ECKey.ECDSASignature Represents an arbitrary ECDSA signature

Signatures in Bitcoin Transaction Format

Bitcoin’s internal signature verification algorithm ( CHECK_SIG ), does not work with standard DER-encoded ECDSA signatures. Instead, a special encoding format is used. This encoding format consists of a standard DER-encoded ECDSA signature, with an additional byte appended.

The extra byte at the end is a flag that indicates which of the SIGHASH algorithms were used in constructing the signature.

Reconstruct a Signature from “Transaction-Format”

import org.twostack.bitcoin4j.transaction.TransactionSignature

/* for more complete examples have a look at these unit tests
https://github.com/twostack/bitcoin4j/blob/main/src/test/java/org/twostack/bitcoin4j/transaction/LockUnlockBuilderTests.java
*/

val sigHex = "3046022100bb3c194a30e460d..." //truncated value
val txSig : TransactionSignature  = TransactionSignature.fromTxFormat(sigHex);

Get “Transaction-Format” of a Signature

import org.twostack.bitcoin4j.transaction.TransactionSignature

val sigHex = "3046022100bb3c194a30e460d..." //truncated value
val txSig : TransactionSignature  = TransactionSignature.fromTxFormat(sigHex);

val newSigHex: String = HEX.encode(txSig.toTxFormat())

Signatures in DER Format

To create standard DER-formatted signatures, do the following:

import java.math.BigInteger
import org.twostack.bitcoin4j.ECKey

//create a signature from (r,s) components
val r = BigInteger("63173831029936981022572627018246571655303050627048489594159321588908385378810", 10)
val s = BigInteger("4331694221846364448463828256391194279133231453999942381442030409253074198130", 10)

val sig = ECKey.ECDSASignature(r, s)

//create a hexadecimal string of the DER-encoded signature
val derHex = HEX.encode(sig.encodeToDER())

Signing and Verification

In the following examples we will assume signing using arbitrary data. There is another method of signing which is specific to Transactions. We cover that method in the Transactions section.

Signing

import org.twostack.bitcoin4j.ECKey
import org.twostack.bitcoin4j.Sha256Hash


//a random private/public keypair
val randomKey = ECKey()

val buffer: ByteArray = "some data".toByteArray()

//create the signature
val sig : ECKey.ECDSASignature = randomKey.sign(Sha256Hash.wrap(buffer))

Verification

import org.twostack.bitcoin4j.ECKey
import org.twostack.bitcoin4j.ECKey.ECDSASignature
import org.twostack.bitcoin4j.crypto.DumpedPrivateKey

val privkeyWif = "92shANodC6Y4evT5kFzjNFQAdjqTtHAnDTLzqBBq4BbKUPyx6CD";
val ecKey : ECKey  = DumpedPrivateKey.fromBase58(NetworkType.TEST, privkeyWif).getKey();

val derHex = "3044022075fc517e541bd54769c080b64397e32161c850f6c1b2b67a5c433affbb3e62770220729e85cc46ffab881065ec07694220e71d4df9b2b8c8fd12c3122cf3a5efbcf2"

val sig : ECDSASignature = ECDSASignature.decodeFromDER(HEX.decode(derHex))

val hash = Sha256Hash.wrap("some data".toByteArray())
val isValid = ecKey.verify(hash, sig)