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)