Skip to content

Instantly share code, notes, and snippets.

@trcio
Created November 9, 2014 17:00
Show Gist options
  • Save trcio/a23a6cf5a154a8cd5aa9 to your computer and use it in GitHub Desktop.
Save trcio/a23a6cf5a154a8cd5aa9 to your computer and use it in GitHub Desktop.
Requires a reference to http://www.bouncycastle.org/csharp/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Runtime.Serialization;
using System.Security.Cryptography;
using System.Xml.Serialization;
using Org.BouncyCastle.Asn1.Sec;
namespace BitcoinTools
{
public static class Bitcoin
{
private static readonly RNGCryptoServiceProvider Rand;
static Bitcoin()
{
Rand = new RNGCryptoServiceProvider();
}
public static BitcoinAddress GenerateBitcoinAddress()
{
var priv = GeneratePrivateKey();
var pub = GetPublicKeyFromPrivateKey(priv);
var addr = GetAddressFromPublicKey(pub);
return new BitcoinAddress(priv, pub, addr);
}
public static string GeneratePrivateKey()
{
var randomBytes = new byte[32];
Rand.GetBytes(randomBytes);
var privateKey = BytesToString(randomBytes);
while (!IsValidPrivateKey(privateKey))
{
Rand.GetBytes(randomBytes);
privateKey = BytesToString(randomBytes);
}
return privateKey;
}
public static bool IsValidPrivateKey(string privateKey)
{
var bigInt = new Org.BouncyCastle.Math.BigInteger(StringToBytes(privateKey));
return bigInt.SignValue != -1;
}
public static string GetAddressFromPrivateKey(string privateKey)
{
return GetAddressFromPublicKey(GetPublicKeyFromPrivateKey(privateKey));
}
public static string GetAddressFromPublicKey(string publicKey)
{
var publicKeyBytes = StringToBytes(publicKey);
var publicKeyRipeHash = RipeMd160Bytes(Sha256Bytes(publicKeyBytes));
var publicKeyRipeWithVersion = ConcatArrays(new byte[] {0x00}, publicKeyRipeHash);
var checkSumHash = Sha256Bytes(Sha256Bytes(publicKeyRipeWithVersion));
var checkSum = new byte[4];
Buffer.BlockCopy(checkSumHash, 0, checkSum, 0, 4);
var binaryAddress = ConcatArrays(publicKeyRipeWithVersion, checkSum);
return Base58Encode(binaryAddress);
}
public static string GetPublicKeyFromPrivateKey(string privateKey)
{
var privateKeyBytes = StringToBytes(privateKey);
var provider = SecNamedCurves.GetByName("secp256k1");
var db = new Org.BouncyCastle.Math.BigInteger(privateKeyBytes);
var dd = provider.G.Multiply(db);
var publicKeyBytes = new byte[65];
var y = dd.Y.ToBigInteger().ToByteArray();
var x = dd.X.ToBigInteger().ToByteArray();
Buffer.BlockCopy(y, 0, publicKeyBytes, 64 - y.Length + 1, y.Length);
Buffer.BlockCopy(x, 0, publicKeyBytes, 32 - x.Length + 1, x.Length);
publicKeyBytes[0] = 4;
return BytesToString(publicKeyBytes);
}
private static byte[] Sha256Bytes(byte[] data)
{
using (var sha = new SHA256Managed())
{
return sha.ComputeHash(data);
}
}
private static byte[] RipeMd160Bytes(byte[] data)
{
using (var ripemd = new RIPEMD160Managed())
{
return ripemd.ComputeHash(data);
}
}
private static T[] ConcatArrays<T>(params T[][] arrays)
{
var result = new T[arrays.Sum(arr => arr.Length)];
var offset = 0;
foreach (var arr in arrays)
{
Buffer.BlockCopy(arr, 0, result, offset, arr.Length);
offset += arr.Length;
}
return result;
}
private static byte[] StringToBytes(string hex)
{
return Enumerable.Range(0, hex.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
.ToArray();
}
private static string BytesToString(byte[] bytes)
{
return BitConverter.ToString(bytes).Replace("-", "").ToLowerInvariant();
}
private static string Base58Encode(IList<byte> data)
{
const string Digits = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
var intData = data.Aggregate<byte, BigInteger>(0, (current, t) => current * 256 + t);
var result = string.Empty;
while (intData > 0)
{
var remainder = (int)(intData % 58);
intData /= 58;
result = Digits[remainder] + result;
}
for (var i = 0; i < data.Count && data[i] == 0; i++)
{
result = '1' + result;
}
return result;
}
}
[DataContract]
public class BitcoinAddress
{
[DataMember(Name="PrivateKey")]
public string PrivateKey { get; set; }
[XmlIgnore]
public string PublicKey { get; set; }
[DataMember(Name="Address")]
public string Address { get; set; }
public BitcoinAddress(string privateKey, string publicKey, string address)
{
PrivateKey = privateKey;
PublicKey = publicKey;
Address = address;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment