Search code examples
c#bitcoinnbitcoin

Sign message with Bitcoin private key


I need to create a bitcoin family text signing tool.

After researching the issue, I found the NBitcoin package and used it to implement the following code:

using System;
using System.Data;
using System.Text;

using NBitcoin;
using NBitcoin.Crypto;
using NBitcoin.DataEncoders;

class Program
{
    static void Main(string[] args)
    {
        var privateKey = new Key();
        var bitcoinPrivateKey = privateKey.GetWif(Network.Main);
        var bitcoinPublicKey = bitcoinPrivateKey.PubKey;
        var address = bitcoinPublicKey.GetAddress(ScriptPubKeyType.Segwit, Network.Main);
        Console.WriteLine(address);

        var message = "test";
        byte[] messageBytes = Encoding.UTF8.GetBytes(message);
        uint256 hash = new uint256(Hashes.SHA256(messageBytes));
        
        var signature = privateKey.Sign(hash);
        var signatureBytes = signature.ToDER();

        string signatureBase64 = Encoders.Base64.EncodeData(signatureBytes);
        Console.WriteLine("Signature: " + signatureBase64);
    }
}

but for some reason the generated signature is not valid for https://www.verifybitcoinmessage.com/

**updated

according to chatgpt i need to sign messages using Bitcoin Message Signing standards

static void Main(string[] args)
{
    var privateKey = new Key();
    var bitcoinPrivateKey = privateKey.GetWif(Network.Main);
    var bitcoinPublicKey = bitcoinPrivateKey.PubKey;
    var address = bitcoinPublicKey.GetAddress(ScriptPubKeyType.Legacy, Network.Main);
    Console.WriteLine("Address: " + address);

    var message = "test";

    // Sign the message using the Bitcoin Message Signing standard
    string signatureBase64 = SignBitcoinMessage(privateKey, message);

    Console.WriteLine("Signature: " + signatureBase64);
}

/// <summary>
/// Signs a message using the Bitcoin Message Signing standard.
/// This method prepares the message with the appropriate prefix and length encoding, 
/// hashes it twice with SHA-256, and then signs it using the provided private key.
/// The resulting signature is returned in Base64 format.
/// </summary>
/// <param name="privateKey">The private key to sign the message with.</param>
/// <param name="message">The message to be signed.</param>
/// <returns>A Base64 encoded signature of the message.</returns>
static string SignBitcoinMessage(Key privateKey, string message)
{
    byte[] messageBytes = Encoding.UTF8.GetBytes(message);

    // Structure for the message in Bitcoin Signed Message format
    byte[] prefixBytes = Encoding.UTF8.GetBytes("Bitcoin Signed Message:\n");
    byte[] lengthBytes = new VarInt((ulong)messageBytes.Length).ToBytes();

    byte[] fullMessage = new byte[prefixBytes.Length + lengthBytes.Length + messageBytes.Length];
    Buffer.BlockCopy(prefixBytes, 0, fullMessage, 0, prefixBytes.Length);
    Buffer.BlockCopy(lengthBytes, 0, fullMessage, prefixBytes.Length, lengthBytes.Length);
    Buffer.BlockCopy(messageBytes, 0, fullMessage, prefixBytes.Length + lengthBytes.Length, messageBytes.Length);

    // Double SHA-256 hashing
    uint256 hash = new uint256(Hashes.SHA256(Hashes.SHA256(fullMessage)));

    ECDSASignature signature = privateKey.Sign(hash);

    // Serialize and format the signature
    var sigBytes = signature.ToCompact();
    byte[] fullSignature = new byte[sigBytes.Length + 1];
    fullSignature[0] = (byte)(27 + 4 + (privateKey.PubKey.IsCompressed ? 4 : 0)); // Include key flag
    Buffer.BlockCopy(sigBytes, 0, fullSignature, 1, sigBytes.Length);

    return Encoders.Base64.EncodeData(fullSignature);
}

but that didn't help either


Solution

  • I used wrong package. The correct package is NBitcoin-std2

    using NBitcoin;
    
    Key Key = new Key(); //Create private key
    //We can take private key
    var privateKey = Key.GetBitcoinSecret(Network.Main);
    
    //Get the public key, and derive the address on the Main network
    BitcoinAddress address = privateKey.PubKey.GetAddress(ScriptPubKeyType.Segwit, Network.Main);
    Console.WriteLine("Address:" + address + "\n");
    
    //For the sign to data , create secret object.
    BitcoinSecret secret = new BitcoinSecret(Key, Network.Main);
    
    string message = $"test";
    Console.WriteLine("Message:" + message + "\n");
    //sign message with private key.
    string signature = secret.PrivateKey.SignMessage(message);
    Console.WriteLine("Signature:" + signature + "\n");