Created
November 9, 2014 17:00
-
-
Save trcio/a23a6cf5a154a8cd5aa9 to your computer and use it in GitHub Desktop.
Requires a reference to http://www.bouncycastle.org/csharp/
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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