public static ExtendedKey Parse(string serialized) { byte[] data = Base58Encoding.DecodeWithCheckSum(serialized); Thrower.Condition <AddressException>(data.Length != 78, "invalid extended key"); using (var stream = new MemoryStream(data)) { using (var binReader = new BinaryReader(stream)) { var ext = binReader.ReadBytes(4); Thrower.Condition <AddressException>(!(ext.SequenceEqual(Xprv) || ext.SequenceEqual(Xpub)), "invalid magic number for an extended key"); var isPrivate = ext.SequenceEqual(Xprv); var depth = binReader.ReadByte(); var fingerprint = binReader.ReadBytes(4); var index = BitHelper.ToUInt32(binReader.ReadBytes(4), false); var chainCode = binReader.ReadBytes(32); var rawKey = binReader.ReadBytes(33); BitcoinKey key; if (isPrivate) { key = new BitcoinPrivateKey(rawKey.Skip(1).ToArray()); } else { key = new BitcoinPublicKey(rawKey); } return(new ExtendedKey(key, chainCode, depth, fingerprint, index)); } } }
/// <summary> /// Initializes a new instance of the <see cref="BitcoinPrivateKey"/> class. /// Parses the given private key as created by the BitCoin C++ RPC. /// </summary> /// <param name="coinParameters"> /// The expected header parameters of the key. If you don't care, provide null. /// </param> /// <param name="encoded"> /// The base58 encoded string. /// </param> public BitcoinPrivateKey(CoinParameters coinParameters, string encoded) { var decoded = Base58Encoding.DecodeWithCheckSum(encoded); var version = decoded.First(); Thrower.Condition <ArgumentException>(coinParameters.PrivateKeyVersion != version, string.Format("Mismatched version number, trying to cross networks? expected={0} found={1}", coinParameters.PrivateKeyVersion, version)); var bytes = decoded.Skip(1).ToArray(); this.Compressed = false; if (bytes.Length == 33) { // private key associated with a compressed public key Thrower.Condition <ArgumentException>(bytes.Last() != CompressedBytes.First(), string.Format("Invalid private key")); bytes = bytes.Take(32).ToArray(); this.Compressed = true; } // 256 bit keys Thrower.Condition <ArgumentException>(bytes.Length != Length, string.Format("Keys are 256 bits, so you must provide {0} bytes, got {1}", Length, bytes.Length)); this.Bytes = bytes; }
private ExtendedKey GenerateKey(uint index) { Thrower.Condition <AddressException>((index & HardendIndex) != 0 && !this.Master.HasPrivateKey, "A public key can't derivate an hardened child"); byte[] extended; byte[] pub = this.Master.PublicKey.Bytes; if ((index & HardendIndex) == 0) { var sequenceBytes = BitHelper.GetBytes(index, false); extended = pub.ToArray().Concat(sequenceBytes).ToArray(); } else { var priv = this.Master.PrivateKey.Bytes; var sequenceBytes = BitHelper.GetBytes(index, false); extended = (new byte[] { 0 }).Concat(priv.ToArray()).Concat(sequenceBytes).ToArray(); } var leftRight = CryptoUtil.ComputeHmac512(this.ChainCode, extended); var leftKey = leftRight.Take(32).ToArray(); var rightKey = leftRight.Skip(32).ToArray(); BigInteger bigIntegerLeft = new BigInteger(1, leftKey); Thrower.Condition <AddressException>(bigIntegerLeft.CompareTo(EcKey.EcParams.N) >= 0, "This is rather unlikely, but it did just happen"); if (this.Master.HasPrivateKey) { BigInteger key = bigIntegerLeft.Add(new BigInteger(1, this.Master.PrivateKey.Bytes)).Mod(EcKey.EcParams.N); Thrower.Condition <AddressException>(key.Equals(BigInteger.Zero), "This is rather unlikely, but it did just happen"); //// fix the private key in case it needs padding var keyBytes = new EcKey(key).GetPrivKeyBytes(); return(new ExtendedKey(new BitcoinPrivateKey(keyBytes), rightKey, (byte)(this.Depth + 1), this.GenerateFingerPrint(), index)); } else { var qdecoded = EcKey.EcParams.Curve.DecodePoint(this.Master.PublicKey.Bytes); var key = new ECPublicKeyParameters("EC", qdecoded, EcKey.EcParams); var qkey = EcKey.EcParams.G.Multiply(bigIntegerLeft).Add(key.Q); Thrower.Condition <AddressException>(qkey.IsInfinity, "This is rather unlikely, but it did just happen"); var point = new FpPoint(EcKey.EcParams.Curve, qkey.Normalize().XCoord, qkey.Normalize().YCoord, true); return(new ExtendedKey(new BitcoinPublicKey(point.GetEncoded()), rightKey, (byte)(this.Depth + 1), this.GenerateFingerPrint(), index)); } }
/// <summary> /// Initializes a new instance of the <see cref="BitcoinPrivateKey"/> class. /// </summary> /// <param name="keyBytes"> /// The key bytes. /// </param> public BitcoinPrivateKey(byte[] keyBytes) : base(keyBytes) { // 256 bit keys Thrower.Condition <ArgumentException>(keyBytes.Length != Length, string.Format("Keys are 256 bits, so you must provide {0} bytes, got {1}", Length, keyBytes.Length)); }