Example #1
0
        /// <summary>
        /// Verify a
        /// </summary>
        /// <param name="pubKey"></param>
        /// <param name="hash"></param>
        /// <param name="signature"></param>
        /// <returns></returns>
        public static bool VerifyHashSignatureSecp256k1(string pubKey, string hash, string signature)
        {
            var secp256k1 = ECCurve.CreateFromValue("1.3.132.0.10");

            // signature is DER encoded -> convert to 64 byte array
            var sigBytes = HexStringToByteArray(signature);

            var p1len = sigBytes[3];
            var sigp1 = sigBytes.Skip(4).SkipWhile(b => b == 0).Take(32).ToArray();             // Remove any 0 padded bytes
            var p2len = sigBytes.Skip(4 + p1len + 1).Take(1).ToArray()[0];
            var sigp2 = sigBytes.Skip(4 + p1len + 2).SkipWhile(b => b == 0).Take(32).ToArray(); // Remove any 0 padded bytes
            var sig   = sigp1.Concat(sigp2).ToArray();

            PubKey pk      = new PubKey(HexStringToByteArray(pubKey));
            var    pkBytes = pk.Decompress().ToBytes();

            using (var dsa = ECDsa.Create(new ECParameters
            {
                Curve = secp256k1,
                Q = new ECPoint
                {
                    // gets the {x,y} from the uncompressed public key
                    X = pkBytes.Skip(1).Take(32).ToArray(),
                    Y = pkBytes.Skip(33).ToArray(),
                }
            }))
            {
                var isValid = dsa.VerifyHash(HexStringToByteArray(hash), sig);

                return(isValid);
            }
        }
        string IAddressGenerator.GenerateAddress(byte[] pubKeyBytes)
        {
            var pubKey            = new PubKey(pubKeyBytes);
            var decompPubKeyBytes = pubKey.Decompress().ToBytes();

            return(Address.WithNetwork(Network.Mainnet).NewSecp256k1Address(decompPubKeyBytes).ToString());
        }
Example #3
0
        public bool Check(string passphrase, BitcoinAddress expectedAddress)
        {
            //Derive passfactor using scrypt with ownerentropy and the user's passphrase and use it to recompute passpoint
            var passfactor =
                BitcoinEncryptedSecretEC.CalculatePassFactor(passphrase, this.LotSequence, this.OwnerEntropy);
            //Derive decryption key for pointb using scrypt with passpoint, addresshash, and ownerentropy
            var passpoint = BitcoinEncryptedSecretEC.CalculatePassPoint(passfactor);
            var derived   =
                BitcoinEncryptedSecretEC.CalculateDecryptionKey(passpoint, this.AddressHash, this.OwnerEntropy);

            //Decrypt encryptedpointb to yield pointb
            var pointbprefix = this.EncryptedPointB[0];

            pointbprefix = (byte)(pointbprefix ^ (byte)(derived[63] & 0x01));

            //Optional since ArithmeticException will catch it, but it saves some times
            if (pointbprefix != 0x02 && pointbprefix != 0x03)
            {
                return(false);
            }
            var pointb = BitcoinEncryptedSecret.DecryptKey(this.EncryptedPointB.Skip(1).ToArray(), derived);

            pointb = new[] { pointbprefix }.Concat(pointb).ToArray();

            //4.ECMultiply pointb by passfactor. Use the resulting EC point as a public key
            var     curve = ECKey.Secp256k1;
            ECPoint pointbec;

            try
            {
                pointbec = curve.Curve.DecodePoint(pointb);
            }
            catch (ArgumentException)
            {
                return(false);
            }
            catch (ArithmeticException)
            {
                return(false);
            }

            var pubkey = new PubKey(pointbec.Multiply(new BigInteger(1, passfactor)).GetEncoded());

            //and hash it into address using either compressed or uncompressed public key methodology as specifid in flagbyte.
            pubkey = this.IsCompressed ? pubkey.Compress() : pubkey.Decompress();

            var actualhash   = BitcoinEncryptedSecretEC.HashAddress(pubkey.GetAddress(this.Network));
            var expectedhash = BitcoinEncryptedSecretEC.HashAddress(expectedAddress);

            return(Utils.ArrayEqual(actualhash, expectedhash));
        }
Example #4
0
        public static string generateAddress()
        {
            // Currently we get PrivateKey and PubKey and Wif from the NBitcoin library. Haven't written my own code yet.
            // https://github.com/MetacoSA/NBitcoin (available from the Nu-Get package manager)

            Key           privateKey = new Key(); // generate a random private key
            BitcoinSecret wif        = privateKey.GetWif(Network.Main);
            PubKey        pubKey     = privateKey.PubKey;
            string        publicKey  = pubKey.ToString(); //"03c18bc24ea4b6e1fc08c356f436d9ffaca4f583c0adc0c83a36b3a1b3abeef762";
            //Console.WriteLine("Publickey: " + publicKey);

            //var hash = getHashSha256(publicKeyHex);
            var hash = getHash("sha256", publicKey);
            //Console.WriteLine("hash:" + hash);
            //Console.ReadLine();

            //RIPEMD160 r = RIPEMD160Managed.Create("SHA256");
            string hashed = getHash("ripemd160", hash);

            //Console.WriteLine("hashed:" + hashed);
            hashed = "3c" + hashed;
            //Console.WriteLine("<hashed:" + hashed);
            //Console.ReadLine();

            string newhash1 = getHash("sha256", hashed);
            string newhash2 = getHash("sha256", newhash1);
            string checksum = newhash2.Substring(0, 8); // 4 hex-bytes = 8 characters
            //Console.WriteLine("first hash256: " + newhash1);
            //Console.WriteLine("2nd   hash256: " + newhash2);
            //Console.WriteLine("Checksum: " + checksum);
            //Console.ReadLine();

            string withChecksum = hashed + checksum;
            string address      = ConvertToBase58(withChecksum);


            //Console.WriteLine(address);

            //var r = ripemd160(Crypto.SHA256(Crypto.util.hexToBytes(h), { asBytes: true}));
            //r.unshift(byte || coinjs.pub);
            //var hash = Crypto.SHA256(Crypto.SHA256(r, { asBytes: true}), { asBytes: true});
            //var checksum = hash.slice(0, 4);
            //return coinjs.base58encode(r.concat(checksum))

            return(wif + "," + privateKey.ToHex() + "," + pubKey.Decompress().ToString() + "," + address + "\n");
        }
        //Referenced By: https://github.com/MetacoSA/NBitcoin/issues/565
        public string GenerateAddress(byte[] pubKeyBytes)
        {
            var ETH_publickKey = new PubKey(pubKeyBytes);

            byte[] byte_ETH_publicKey = ETH_publickKey.Decompress().ToBytes();

            var PubKeyNoPrefix = new byte[byte_ETH_publicKey.Length - 1];

            Array.Copy(byte_ETH_publicKey, 1, PubKeyNoPrefix, 0, PubKeyNoPrefix.Length);
            var initaddr = new Nethereum.Util.Sha3Keccack().CalculateHash(PubKeyNoPrefix);
            var addr     = new byte[initaddr.Length - 12];

            Array.Copy(initaddr, 12, addr, 0, initaddr.Length - 12);
            var    hex_addr       = BitConverter.ToString(addr).Replace("-", "");
            string public_address = new Nethereum.Util.AddressUtil().ConvertToChecksumAddress(hex_addr);

            return(public_address);
        }
        public void ShouldGeneratePublicKeyFromPrivateKey()
        {
            var priv = "181ff77d7286b81334b7166f290c0a509b5106c21505aba3f004bbc3bd78027b";

            Wallet wallet       = new FileCoinWallet(priv);
            var    pubKeyDecomp = wallet.PublicKey.Decompress();

            Assert.AreEqual(
                expected: "04e2c906303ddcc8a52c6e9e42c5c83c1e43fc4ca3e15357ae85478adbc029cacdddf97830cf46720d41e04ab4592b7db219fda58f289e1df170d34fd174672cc3",
                actual: pubKeyDecomp.ToHex()
                );

            var pubKey = new PubKey(wallet.PublicKey.ToBytes());

            Assert.AreEqual(
                expected: "04e2c906303ddcc8a52c6e9e42c5c83c1e43fc4ca3e15357ae85478adbc029cacdddf97830cf46720d41e04ab4592b7db219fda58f289e1df170d34fd174672cc3",
                actual: pubKey.Decompress().ToHex()
                );
        }
        public string GenerateAddress(byte[] pubKeyBytes, Network network, Protocol protocol)
        {
            var pubKey            = new PubKey(pubKeyBytes);
            var decompPubKeyBytes = pubKey.Decompress().ToBytes();

            var address = Address.WithNetwork(network);

            switch (protocol)
            {
            case Protocol.SECP256K1:
                address = address.NewSecp256k1Address(decompPubKeyBytes);
                break;

            default:
                throw new NotSupportedException();
            }

            return(address.ToString());
        }
Example #8
0
        string GenerateAddress(PubKey pubKey, NetworkVersion networkVersion)
        {
            var minLength   = -1;
            var publicKey   = pubKey.Decompress();
            var pubKeyBytes = publicKey.ToBytes();

            var version    = (int)networkVersion;
            var versionHex = Convert.ToString(version, 16);

            var pubKeyHash160BytesSha256 = NBitcoin.Crypto.Hashes.SHA256(pubKeyBytes);
            var pubKeyHash160Bytes       = NBitcoin.Crypto.Hashes.RIPEMD160(pubKeyHash160BytesSha256, pubKeyHash160BytesSha256.Length);
            var pubKeyHash160Hex         = pubKeyHash160Bytes.ToHexString();

            var hash160Regex = new Regex(@"/^[0-9a-fA-F]{40}$/");

            if (hash160Regex.Match(pubKeyHash160Hex).Length > 0)
            {
                throw new ArgumentException("Invalid argument: not a hash160 hex string");
            }

            var checksumInputHex   = $"{versionHex}{pubKeyHash160Hex}";
            var checksumInputBytes = checksumInputHex.FromHexToByteArray();

            if (version < 0 || version >= 32)
            {
                throw new ArgumentOutOfRangeException("Invalid version (must be between 0 and 31)");
            }

            if (versionHex.Length == 1)
            {
                versionHex = $"0{versionHex}";
            }

            var checksumRegex = new Regex(@"/^[0-9a-fA-F]*$/");

            if (checksumRegex.Match(checksumInputHex).Length > 0)
            {
                throw new ArgumentException("Invalid data (not a hex string)");
            }

            var doubleHashedChecksumInputBytes = NBitcoin.Crypto.Hashes.SHA256(NBitcoin.Crypto.Hashes.SHA256(checksumInputBytes));
            var checksumHex = doubleHashedChecksumInputBytes[..4].ToHexString();
        string GenerateAddress(PubKey pubKey)
        {
            var publicKey = pubKey.Decompress();

            var pubKeyBytes = publicKey.ToBytes();

            pubKeyBytes = pubKeyBytes.Skip(1).ToArray();

            var pubKeyHash = new Sha3Keccack().CalculateHash(pubKeyBytes);

            var sha3HashBytes = new byte[20];

            Array.Copy(pubKeyHash, pubKeyHash.Length - 20, sha3HashBytes, 0, 20);

            byte[] PKHWithVersionBytes = Helper.Concat(new byte[] { 65 }, sha3HashBytes);
            var    hexAddress          = PKHWithVersionBytes.ToHexString();

            var address = Encoders.Base58Check.EncodeData(PKHWithVersionBytes);

            return(address);
        }
Example #10
0
        public static String GetETHLedgerAddress(ExtPubKey pubkey, int index)
        {
            var pkd = pubkey;

            PubKey ETH_publickKey = pkd.PubKey;

            byte[] byte_ETH_publicKey = ETH_publickKey.Decompress().ToBytes();

            var PubKeyNoPrefix = new byte[byte_ETH_publicKey.Length - 1];

            Array.Copy(byte_ETH_publicKey, 1, PubKeyNoPrefix, 0, PubKeyNoPrefix.Length);

            var initaddr = new Nethereum.Util.Sha3Keccack().CalculateHash(PubKeyNoPrefix);
            var addr     = new byte[initaddr.Length - 12];

            Array.Copy(initaddr, 12, addr, 0, initaddr.Length - 12);

            var    hex_addr       = BitConverter.ToString(addr).Replace("-", "");
            string public_address = new Nethereum.Util.AddressUtil().ConvertToChecksumAddress(hex_addr);

            return(public_address);
        }
Example #11
0
        static void Main(string[] args)
        {
            // Number of BTC and ETH keys to generate
            int n = 100;

            Console.WriteLine("");

            // Generate Bitcoin addresses and private keys
            Console.WriteLine("Generating " + n + " BTC addresses & private keys...");
            Console.WriteLine("");

            string BTCfileName = "C:\\GENKEYS\\BitcoinKeys.csv";

            if (!File.Exists(BTCfileName))
            {
                string header = "ID, BTC address, BTC privateKey, Assigned" + Environment.NewLine;
                File.WriteAllText(BTCfileName, header);
            }
            for (int i = 1; i <= n; i++)
            {
                try
                {
                    Guid guid = Guid.NewGuid();

                    Key    privateKey = new Key();         // generate a random private key
                    PubKey publicKey  = privateKey.PubKey; // derive public key

                    var privateKeySecret = privateKey.GetBitcoinSecret(Network.Main);
                    var address          = publicKey.Decompress().GetAddress(Network.Main);

                    string keys = guid + "," + address + "," + privateKeySecret + ", 0" + Environment.NewLine;

                    File.AppendAllText(BTCfileName, keys);

                    Console.WriteLine("BTC address: " + address);
                }
                catch (Exception x)
                {
                    Console.WriteLine("Error:" + x.Message);
                    Console.WriteLine("");
                }
            }

            // --------------------------------------------
            Console.WriteLine("");
            Console.WriteLine("");
            Console.WriteLine("");
            // --------------------------------------------

            // Generate Ethereum addresses and private keys
            Console.WriteLine("Generating " + n + " ETH addresses & private keys...");
            Console.WriteLine("");

            string ETHfileName = "C:\\GENKEYS\\EthereumKeys.csv";

            if (!File.Exists(ETHfileName))
            {
                string header = "ID, ETH address, ETH privateKey, Assigned" + Environment.NewLine;
                File.WriteAllText(ETHfileName, header);
            }
            for (int i = 1; i <= n; i++)
            {
                try
                {
                    Guid guid = Guid.NewGuid();

                    var ecKey             = EthECKey.GenerateKey();               // generate a random private key
                    var privateKeyAsBytes = ecKey.GetPrivateKeyAsBytes().ToHex(); // derive public key
                    var address           = ecKey.GetPublicAddress();

                    string keys = guid + "," + address + "," + privateKeyAsBytes + ", 0" + Environment.NewLine;

                    File.AppendAllText(ETHfileName, keys);

                    Console.WriteLine("ETH address: " + address);
                }
                catch (Exception x)
                {
                    Console.WriteLine("Error:" + x.Message);
                    Console.WriteLine("");
                }
            }
        }
        public EncryptedKeyResult GenerateEncryptedSecret(bool isCompressed = true, byte[] seedb = null)
        {
            //Set flagbyte.
            byte flagByte = 0;

            //Turn on bit 0x20 if the Bitcoin address will be formed by hashing the compressed public key
            flagByte |= isCompressed ? (byte)0x20 : (byte)0x00;
            flagByte |= LotSequence != null ? (byte)0x04 : (byte)0x00;

            //Generate 24 random bytes, call this seedb. Take SHA256(SHA256(seedb)) to yield 32 bytes, call this factorb.
            seedb = seedb ?? RandomUtils.GetBytes(24);

            var factorb = Hashes.Hash256(seedb).ToBytes();

            //ECMultiply passpoint by factorb.
            var curve     = ECKey.Secp256k1;
            var passpoint = curve.Curve.DecodePoint(Passpoint);
            var pubPoint  = passpoint.Multiply(new BigInteger(1, factorb));

            //Use the resulting EC point as a public key
            var pubKey = new PubKey(pubPoint.GetEncoded());

            //and hash it into a Bitcoin address using either compressed or uncompressed public key
            //This is the generated Bitcoin address, call it generatedaddress.
            pubKey = isCompressed ? pubKey.Compress() : pubKey.Decompress();

            //call it generatedaddress.
            var generatedaddress = pubKey.GetAddress(Network);

            //Take the first four bytes of SHA256(SHA256(generatedaddress)) and call it addresshash.
            var addresshash = BitcoinEncryptedSecretEC.HashAddress(generatedaddress);

            //Derive a second key from passpoint using scrypt
            //salt is addresshash + ownerentropy
            var derived = BitcoinEncryptedSecretEC.CalculateDecryptionKey(Passpoint, addresshash, OwnerEntropy);

            //Now we will encrypt seedb.
            var encrypted = BitcoinEncryptedSecret.EncryptSeed
                                (seedb,
                                derived);

            //0x01 0x43 + flagbyte + addresshash + ownerentropy + encryptedpart1[0...7] + encryptedpart2 which totals 39 bytes
            var bytes =
                new[] { flagByte }
            .Concat(addresshash)
            .Concat(this.OwnerEntropy)
            .Concat(encrypted.Take(8).ToArray())
            .Concat(encrypted.Skip(16).ToArray())
            .ToArray();

            var encryptedSecret = new BitcoinEncryptedSecretEC(bytes, Network);

            return(new EncryptedKeyResult(encryptedSecret, generatedaddress, seedb, () =>
            {
                //ECMultiply factorb by G, call the result pointb. The result is 33 bytes.
                var pointb = new Key(factorb).PubKey.ToBytes();
                //The first byte is 0x02 or 0x03. XOR it by (derivedhalf2[31] & 0x01), call the resulting byte pointbprefix.
                var pointbprefix = (byte)(pointb[0] ^ (byte)(derived[63] & 0x01));
                var pointbx = BitcoinEncryptedSecret.EncryptKey(pointb.Skip(1).ToArray(), derived);
                var encryptedpointb = new byte[] { pointbprefix }.Concat(pointbx).ToArray();

                var confirmBytes =
                    Network.GetVersionBytes(Base58Type.CONFIRMATION_CODE)
                    .Concat(new[] { flagByte })
                    .Concat(addresshash)
                    .Concat(OwnerEntropy)
                    .Concat(encryptedpointb)
                    .ToArray();

                return new BitcoinConfirmationCode(Encoders.Base58Check.EncodeData(confirmBytes), Network);
            }));
        }
Example #13
0
        public void TestAddressCreation()
        {
            Key privateKey;                                                                               // = new Key();

            privateKey = Key.Parse("L5e7GTVzDEFTS2YwRJcRse5LkgpAetKApCafc6avoKLovPcnFE74", Network.Main); // For getting the same outputs
            PubKey         publicKey         = privateKey.PubKey;
            BitcoinAddress addressLegacy     = publicKey.GetAddress(ScriptPubKeyType.Legacy, Network.Main);
            BitcoinAddress addressSegwit     = publicKey.GetAddress(ScriptPubKeyType.Segwit, Network.Main);
            BitcoinAddress addressSegwitP2SH = publicKey.GetAddress(ScriptPubKeyType.SegwitP2SH, Network.Main);
            KeyId          publicKeyHash     = publicKey.Hash;

            // Wif -> 
            // Priv. key -> Wif (Check README.md - Wif)
            BitcoinSecret wif  = privateKey.GetWif(Network.Main);
            BitcoinSecret wif2 = new BitcoinSecret("L5e7GTVzDEFTS2YwRJcRse5LkgpAetKApCafc6avoKLovPcnFE74", Network.Main);

            Assert.AreEqual(wif, wif2);

            // Private key -> 
            // Wif -> Priv. key
            Key privateKey1 = Key.Parse("L5e7GTVzDEFTS2YwRJcRse5LkgpAetKApCafc6avoKLovPcnFE74", Network.Main);

            Assert.AreEqual(privateKey, privateKey1);

            // Base58 encoded priv key
            var privKeyVersionBytes = Network.Main.GetVersionBytes(Base58Type.SECRET_KEY, true);

            byte[] privKeyWithVersionBytes = Helper.Concat(privKeyVersionBytes, privateKey.ToBytes());
            var    privKeyBase58           = Encoders.Base58Check.EncodeData(privKeyWithVersionBytes);

            // Encrypted priv key (BIP38)
            BitcoinEncryptedSecret encryptedPrivKey = wif.Encrypt("password");

            // Decrypted priv key
            BitcoinEncryptedSecret encryptedPrivKeyFromStr = BitcoinEncryptedSecret.Create("6PYWRpL1zPnz5kVxn74H9WdGJZVQ3ah6Pnq54GNinkfrXdd9fWNVS6dn95", Network.Main);
            BitcoinSecret          decryptedPrivKey        = encryptedPrivKeyFromStr.GetSecret("password");

            Assert.AreEqual(wif, decryptedPrivKey);

            // Public key hash -> 
            // Check README (Hash160(Public Key))
            var hash1 = NBitcoin.Crypto.Hashes.SHA256(publicKey.ToBytes());
            var pkh   = NBitcoin.Crypto.Hashes.RIPEMD160(hash1, hash1.Length); // SHA256(RIPEMD160(PUB_KUY))
            var generatedPubKeyHash = new KeyId(pkh);

            Assert.AreEqual(publicKeyHash, generatedPubKeyHash);

            // Bitcoin Address > 
            var versionBytes = Network.Main.GetVersionBytes(Base58Type.PUBKEY_ADDRESS, true); // 0x00

            byte[] PKHWithVersionBytes = Helper.Concat(versionBytes, pkh);                    // 0x00 + PKH
            var    address1            = Encoders.Base58Check.EncodeData(PKHWithVersionBytes);

            Assert.AreEqual(addressLegacy.ToString(), address1); // 1Co3ZZ3U5ELVmZrV3oXk2qbv58AjuwRrnB

            Console.WriteLine();
            Console.WriteLine($"Pri : {privateKey.ToHex()} (Hex)");         // fb401b5261327d8543382c065af27e28a9775c278b7c3dba8cd0d88f0ad042b1
            Console.WriteLine($"Pri : {privKeyBase58} (Base58)");           // 5KiwSFcZuAM3N5Ruf8cfiNzdCFupxFoFuqPafmK7JHqtsEms7HB
            Console.WriteLine($"Pri : {encryptedPrivKey} (Encrypted)");     // 6PYWRpL1zPnz5kVxn74H9WdGJZVQ3ah6Pnq54GNinkfrXdd9fWNVS6dn95
            Console.WriteLine($"Pri : {wif} (Wif)");                        // L5e7GTVzDEFTS2YwRJcRse5LkgpAetKApCafc6avoKLovPcnFE74
            Console.WriteLine($"Pub : {publicKey}");                        // 03c092d451383dedd6052de6778f9b7c393252ecbd4cd05e842638a84a2c6e2528
            Console.WriteLine($"Pub : {publicKey.Decompress()} (Decomp.)"); // 04c092d451383dedd6052de6778f9b7c393252ecbd4cd05e842638a84a2c6e2528fe41f265a1d8884eb304a33b6f8a6a245fab83f7bf503d7dfc532ffc6593df15
            Console.WriteLine($"PKH : {publicKey.Hash}");                   // 815ea7e8372ae215c40dc3d07a024adc7ddaf858
            Console.WriteLine($"Add : {addressLegacy} (Legacy)");           // 1Co3ZZ3U5ELVmZrV3oXk2qbv58AjuwRrnB
            Console.WriteLine($"Add : {addressSegwitP2SH} (SegwitP2SH)");   // 3JCvRhjrEyw9pvZiE3TxsdSHNPJQh1vHTe
            Console.WriteLine($"Add : {addressSegwit} (Segwit)");           // bc1qs90206ph9t3pt3qdc0g85qj2m37a47zclpwws7
        }
Example #14
0
        public void TestVerify()
        {
            // Sample challenge-response
            //    Send to wallet:
            // req: lnurl1dp68gup69uhnzwfj9ccnvwpwxqhrzdej8gerwdf5xvhkcmnpw46xstmnd9nku6tw8a6xzeead3hkw6twye4nz0f4xqenqvps89zrxd6xxaryx3fc8yc5y3fcxycyxwzyx3qnydz9xveyyd6pg56r2wpjxscnxd2p8qcrzdzpx9prxd29xapnjdjxx4ryys3t74l
            // url:http://192.168.0.172:27543/lnauth/signin?tag=login&k1=5030009D37F7FCE891BE810C8D4A24E32B7AE45824135A8014A1B35E7C96F5FB
            //    Response from wallet:
            // key: 038b02325d76b1e096071a6fdaf6c695800ad79b7245f7e6b0d5ccd505a2d4c10c
            // k1:5030009D37F7FCE891BE810C8D4A24E32B7AE45824135A8014A1B35E7C96F5FB
            // sig:3044022006a071c4997c313e3c0bcb13daed2dddf1ebe5d899405125e674423c12480150022006664645062a1f2b5bca4d35c76ef72b0cdd46e1ba0cab89bd085c18a2922f6d

            // Works
            //var publicKey = CryptoService.HexStringToByteArray("038b02325d76b1e096071a6fdaf6c695800ad79b7245f7e6b0d5ccd505a2d4c10c");
            //var hash = CryptoService.HexStringToByteArray("5030009D37F7FCE891BE810C8D4A24E32B7AE45824135A8014A1B35E7C96F5FB");
            //var signature = CryptoService.HexStringToByteArray("3044022006a071c4997c313e3c0bcb13daed2dddf1ebe5d899405125e674423c12480150022006664645062a1f2b5bca4d35c76ef72b0cdd46e1ba0cab89bd085c18a2922f6d");

            // Fails - should work
            var publicKey = CryptoService.HexStringToByteArray("038b02325d76b1e096071a6fdaf6c695800ad79b7245f7e6b0d5ccd505a2d4c10c");
            var hash      = CryptoService.HexStringToByteArray("6A6EAB458D4DE401A78F109911274A1A21A9A512BA861933D6937E0EA95B016A");
            var signature = CryptoService.HexStringToByteArray("3045022100da228f52aaeef71ba92b0241832e5e19f23f74bc663057017c35ce81c51efe5702206a0c1c1f56af604ada61557f827ca0778546c25d6729edbd43a8853f9a96435f");

            var secp256k1 = ECCurve.CreateFromValue("1.3.132.0.10");
            // signature is DER encoded -> convert to 64 byte array

            var p1len = signature[3];
            var sigp1 = signature.Skip(4).SkipWhile(b => b == 0).Take(32).ToArray();             // Remove any 0 padded bytes
            var p2len = signature.Skip(4 + p1len + 1).Take(1).ToArray()[0];
            var sigp2 = signature.Skip(4 + p1len + 2).SkipWhile(b => b == 0).Take(32).ToArray(); // Remove any 0 padded bytes
            var sig   = sigp1.Concat(sigp2).ToArray();
            // decompress the public key

            //var i = publicKey[0];
            //var x = new BigInteger(publicKey.Skip(1).ToArray());

            //var p = new BigInteger(CryptoService.HexStringToByteArray("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F"));
            //var a = new BigInteger(0);
            //var b = new BigInteger(7);

            //var y_sq = (x * x * x + a * x + b) % p;

            //var y = BigInteger.ModPow(y_sq, (p + 1) / 4, p);

            //if ((i == 02 && y % 2 != 0) || (i == 03 && y % 2 == 0))
            //{
            //    y = (p - y % p);
            //}

            //var pkX1 = x.ToByteArray();
            //var pkY1 = y.ToByteArray();

            PubKey pk      = new PubKey(publicKey);
            var    pkBytes = pk.Decompress().ToBytes();

            var pkX = pkBytes.Skip(1).Take(32).ToArray();
            var pkY = pkBytes.Skip(33).ToArray();

            var dsa = ECDsa.Create(new ECParameters
            {
                Curve = secp256k1,
                //D optional: (private key bytes) we don't have
                Q = new ECPoint
                {
                    // gets the {x,y} from the uncompressed public key
                    X = pkX,
                    Y = pkY,
                }
            });

            var isValid = dsa.VerifyHash(hash, sig);

            Assert.IsTrue(isValid);
            //CngKey.Import(publicKey, CngKeyBlobFormat.EccPublicBlob)
            //ECDsaCng ecsdKey = new ECDsaCng()
        }