Author

Topic: C# code needed for public-private Bitcoin addresses (Read 381 times)

member
Activity: 210
Merit: 26
High fees = low BTC price
By the way this is the way you get public key from private key using this library:
https://github.com/TangibleCryptography/Secp256k1/blob/master/Secp256k1.Demo/Program.cs#L14
That is a bunch of BigInt multiplications.

Yes that's the same code supplied for testing it out.
Quote
BigInteger privateKey = Hex.HexToBigInteger(privKeyHex);
ECPoint publicKey = Secp256k1.Secp256k1.G.Multiply(privateKey);

Shame both the private address and public address objects are not both of the same type because some times
I want to encrypt with the public and decrypt with private and this works with DSA if you force it.

I was expecting the signature to work like that too but they do something with the public key and put that in the signature
and I've not seen the code to verify the signature that runs on the node and take it that the
bool verified = messageSigner.Verify(signedMessage);
is client-side (Wallet) testing of the signature but maybe I was reading it wrong

compliments to the guy that wrote it anyway
legendary
Activity: 1042
Merit: 2805
Bitcoin and C♯ Enthusiast
Well you wanted an alternative to BouncyCastle, NBitcoin,... which are thoroughly tested. The link I gave you is a small project surely with bugs.
I haven't checked the whole thing myself, I just used the Secp256k1 class for testing.

The reason you are passing false in that method to get it working is probably because your public key is uncompressed whereas in the example and the default mode, the public key is compressed.
For reference: https://github.com/TangibleCryptography/Secp256k1/blob/master/Secp256k1.Core/Extensions/BigIntExtensions.cs#L171-L178

By the way this is the way you get public key from private key using this library:
https://github.com/TangibleCryptography/Secp256k1/blob/master/Secp256k1.Demo/Program.cs#L14
That is a bunch of BigInt multiplications.
member
Activity: 210
Merit: 26
High fees = low BTC price
Hi

Yes good code but using the test code it would not Verify and return true

Quote
if (address == message.Address)
                return true;

publicKey.GetBitcoinAddress() needed false adding to it in the class MessageSignerVerifier

Code:
var signedMessage = new SignedMessage(message, publicKey.GetBitcoinAddress(false), signatureBytes);
member
Activity: 210
Merit: 26
High fees = low BTC price
Well I managed to get something working without using the usual mega big 3rd party solutions if anyone is interested
that works in C# using an extension class that wraps dot.nets RSACryptoServiceProvider

Sample usage tested and shown belew

Code:
string secret = "My secret message";
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(512);  // Key bits length
byte[] secretData = Encoding.UTF8.GetBytes(secret);
//Microsoft RSA byt default will encode with public key and decode with the private key
byte[] encPublic = rsa.PublicEncryption(secretData);
byte[] decPublic = rsa.PublicDecryption(encPublic);
//Now we can also encode with public key and decode with the private keys needed for signatures
byte[] encPrivate = rsa.PrivateEncryption(secretData);
byte[] decPrivate = rsa.PrivateDecryption(encPrivate);
//Just test we got the original data back
string MsgPublic = Encoding.UTF8.GetString(decPublic);  
string MsgPrivate = Encoding.UTF8.GetString(decPrivate);  

Complete listing shown below so just add a class to your project and drop this code in the file and you
should be good to go with most of the credit going to Dudi Bedner

Code:
//All credits go to Dudi Bedner at https://www.codeproject.com/Articles/38739/RSA-Private-Key-Encryption
//This is an extension class and extends RSACryptoServiceProvider so that it will encryt/decrypt using both public and private keys
using System;
using System.Text;
using System.Security.Cryptography;
using System.Numerics;

namespace RSAExtensions
{
    public static class RSAPrivateEncryption
    {
        public static byte[] PublicEncryption(this RSACryptoServiceProvider rsa, byte[] data)
        {
            //rsa.FromXmlString(key);
            byte[] encryptedData = rsa.Encrypt(data, false);
            string base64Encrypted = Convert.ToBase64String(encryptedData);
            return UTF8Encoding.UTF8.GetBytes(base64Encrypted);
        }

        public static byte[] PublicDecryption(this RSACryptoServiceProvider rsa, byte[] cipherData)
        {
            //var resultBytes = Convert.FromBase64String(EncryptedStr);

            string Data = UTF8Encoding.UTF8.GetString(cipherData);
            var resultBytes = Convert.FromBase64String(Data);
            var decryptedBytes =rsa.Decrypt(resultBytes, false);
            string decryptedData = Encoding.UTF8.GetString(decryptedBytes);
            return UTF8Encoding.UTF8.GetBytes(decryptedData);
        }


        public static byte[] PrivateEncryption(this RSACryptoServiceProvider rsa, byte[] data)
        {
            if (data == null)
                throw new ArgumentNullException("data");
            if (rsa.PublicOnly)
                throw new InvalidOperationException("Private key is not loaded");

            int maxDataLength = (rsa.KeySize / 8) - 6;
            if (data.Length > maxDataLength)
                throw new ArgumentOutOfRangeException("data", string.Format(
                    "Maximum data length for the current key size ({0} bits) is {1} bytes (current length: {2} bytes)",
                    rsa.KeySize, maxDataLength, data.Length));

            // Add 4 byte padding to the data, and convert to BigInteger struct
            BigInteger numData = GetBig(AddPadding(data));

            RSAParameters rsaParams = rsa.ExportParameters(true);
            BigInteger D = GetBig(rsaParams.D);
            BigInteger Modulus = GetBig(rsaParams.Modulus);
            BigInteger encData = BigInteger.ModPow(numData, D, Modulus);

            return encData.ToByteArray();
        }

        public static byte[] PrivateDecryption(this RSACryptoServiceProvider rsa, byte[] cipherData)
        {
            if (cipherData == null)
                throw new ArgumentNullException("cipherData");

            BigInteger numEncData = new BigInteger(cipherData);

            RSAParameters rsaParams = rsa.ExportParameters(false);
            BigInteger Exponent = GetBig(rsaParams.Exponent);
            BigInteger Modulus = GetBig(rsaParams.Modulus);

            BigInteger decData = BigInteger.ModPow(numEncData, Exponent, Modulus);

            byte[] data = decData.ToByteArray();
            byte[] result = new byte[data.Length - 1];
            Array.Copy(data, result, result.Length);
            result = RemovePadding(result);

            Array.Reverse(result);
            return result;
        }

        private static BigInteger GetBig(byte[] data)
        {
            byte[] inArr = (byte[])data.Clone();
            Array.Reverse(inArr);  // Reverse the byte order
            byte[] final = new byte[inArr.Length + 1];  // Add an empty byte at the end, to simulate unsigned BigInteger (no negatives!)
            Array.Copy(inArr, final, inArr.Length);

            return new BigInteger(final);
        }

        // Add 4 byte random padding, first bit *Always On*
        private static byte[] AddPadding(byte[] data)
        {
            Random rnd = new Random();
            byte[] paddings = new byte[4];
            rnd.NextBytes(paddings);
            paddings[0] = (byte)(paddings[0] | 128);

            byte[] results = new byte[data.Length + 4];

            Array.Copy(paddings, results, 4);
            Array.Copy(data, 0, results, 4, data.Length);
            return results;
        }

        private static byte[] RemovePadding(byte[] data)
        {
            byte[] results = new byte[data.Length - 4];
            Array.Copy(data, results, results.Length);
            return results;
        }
    }
}




member
Activity: 210
Merit: 26
High fees = low BTC price
Microsoft is useless and I was just skinning some code out to do RSA encryption using this bit of code
that should work on older versions of the DOT.NET framework that don't come with BigINT

Code:
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(384);
_privateKey = rsa.ToXmlString(true);
_publicKey = rsa.ToXmlString(false);
string text = "Hello world";
string encPublic = EncodePublic(_publicKey, text);
string decPrivate = DecodePrivate(_privateKey, encPublic);

string encSig = EncodePublic(_privateKey, text);
string decSigPrivate = DecodePrivate(_privateKey, encSig);
string decSigPublic = DecodePrivate(_publicKey, encSig);  //GOES BANG HERE

public string  EncodePublic(string key,string str)
        {
            RSACryptoServiceProvider RSA2= new RSACryptoServiceProvider();
            RSA2.FromXmlString(key);
            byte[] encryptedData = RSA2.Encrypt(Encoding.UTF8.GetBytes(str), false);
            string base64Encrypted = Convert.ToBase64String(encryptedData);
            return base64Encrypted;
        }

        public string DecodePrivate(string key, string EncryptedStr)
        {
            RSACryptoServiceProvider RSA2 = new RSACryptoServiceProvider();
            RSA2.FromXmlString(key);
            var resultBytes = Convert.FromBase64String(EncryptedStr);
            var decryptedBytes = RSA2.Decrypt(resultBytes, false);
            var decryptedData = Encoding.UTF8.GetString(decryptedBytes);
            return decryptedData;
        }

I want to use the old framework because it will work on more clients but after hours of
looking around i come across this
Quote

RSA Private Key Encryption

Unfortunately, the RSACryptoServiceProvider class does not provide you this option, so I wrote my own implementation of the RSA algorithm using the basics of the RSACryptoServiceProvider in conjunction with Chew Keong TAN's class: BigInteger (http://www.codeproject.com/KB/cs/biginteger.aspx). At a low level, the RSA algorithm is about implementing mathematical equations on huge (huge) integers, so the BigInteger class is really essential. I couldn't have done it myself.

So thanks Microsoft for not throwing an error like "Private key encryption is not possible" instead of "Key not found"
from the bloated framework.

I thought that I wanted private key encryption for the signature when signing transactions and Bitcoin uses this technique
but then I started to read more about it and this guy says https://rdist.root.org/2007/05/03/rsa-public-keys-are-not-private-implementation/
Quote
Finally, I’d like to reiterate what I’ve said before. Public key cryptosystems and RSA in particular are extremely fragile. Do not use them differently than they were designed. Better yet, seek expert review or design assistance when working with any design involving crypto.

What do you guys think ?
member
Activity: 210
Merit: 26
High fees = low BTC price
legendary
Activity: 1042
Merit: 2805
Bitcoin and C♯ Enthusiast
member
Activity: 210
Merit: 26
High fees = low BTC price
I don't want to use external libraries such as BouncyCastle or NBitcoin but don't mind using
Dot.Net framework 4.5 that includes BigINT

Nothing in windows seems easy when it comes to Bitcoin

Edit to add
found this but it wraps BouncyCastle but will compile in VS2017 but it's still not what I am looking for but it does do WIF conversion
if anyone wants it but it Base58 and not 32 like Sewit
https://github.com/Thashiznets/KeyCore.NET





Jump to: