Skip to content

Instantly share code, notes, and snippets.

@Coding-Enthusiast
Created November 23, 2018 04:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Coding-Enthusiast/2aa47cb1b0f59fbc4026cdde25a04c79 to your computer and use it in GitHub Desktop.
Save Coding-Enthusiast/2aa47cb1b0f59fbc4026cdde25a04c79 to your computer and use it in GitHub Desktop.
MiniPrivateKey class from CryptoCurrency.Net library.
using CryptoCurrency.Net.Coins;
using CryptoCurrency.Net.Extensions;
using CryptoCurrency.Net.Hashing;
using System;
using System.Linq;
using System.Text;
namespace CryptoCurrency.Net.KeyPairs
{
/// <summary>
/// These are keys used in some physical bitcoins.
/// <para/>https://en.bitcoin.it/wiki/Mini_private_key_format
/// </summary>
public class MiniPrivateKey
{
/// <summary>
/// Initializes a new instance of <see cref="MiniPrivateKey"/> using the key string.
/// </summary>
/// <exception cref="ArgumentException"/>
/// <param name="key">Key to use (should be 22, 26, or 30 character long and start with 'S')</param>
public MiniPrivateKey(string key)
{
if (!IsValid(key))
{
throw new ArgumentException("Invalid key.");
}
miniKey = key;
bytes = Encoding.UTF8.GetBytes(key);
}
private readonly byte[] bytes;
private readonly string miniKey;
private const string b58Chars = "23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
/// <summary>
/// Initializes a new instance of <see cref="MiniPrivateKey"/> with a randomly generated key.
/// </summary>
/// <remarks>
/// This is the way Casascius does it.
/// https://github.com/casascius/Bitcoin-Address-Utility/blob/e493d51e4a1da7536fc8e8aea38eeaee38abf4cb/Model/MiniKeyPair.cs#L54-L80
/// </remarks>
/// <returns>A new random <see cref="MiniPrivateKey"/>.</returns>
public static MiniPrivateKey CreateRandom()
{
// Create a random 256 bit key. It will only be used for its characters
PrivateKey tempKey = new PrivateKey(new Bitcoin());
string b58 = 'S' + tempKey.ToWif(false, NetworkType.MainNet).Replace("1", "").Substring(4, 29);
char[] chars = b58.ToCharArray();
char[] charstest = (b58 + "?").ToCharArray();
while (HashFunctions.ComputeSha256(Encoding.UTF8.GetBytes(charstest))[0] != 0)
{
// As long as key doesn't pass typo check, increment it.
for (int i = chars.Length - 1; i >= 0; i--)
{
char c = chars[i];
if (c == '9')
{
charstest[i] = chars[i] = 'A';
break;
}
else if (c == 'H')
{
charstest[i] = chars[i] = 'J';
break;
}
else if (c == 'N')
{
charstest[i] = chars[i] = 'P';
break;
}
else if (c == 'Z')
{
charstest[i] = chars[i] = 'a';
break;
}
else if (c == 'k')
{
charstest[i] = chars[i] = 'm';
break;
}
else if (c == 'z')
{
charstest[i] = chars[i] = '2';
// No break - let loop increment prior character.
}
else
{
charstest[i] = chars[i] = ++c;
break;
}
}
}
return new MiniPrivateKey(new string(chars));
}
/// <summary>
/// Checks a given string to see if it is a valid mini-private-key.
/// </summary>
/// <param name="key">Key string to check.</param>
/// <returns>True if valid, false if not.</returns>
public static bool IsValid(string key)
{
if (string.IsNullOrEmpty(key))
{
return false;
}
if (key.Length != 22 && key.Length != 26 && key.Length != 30)
{
return false;
}
if (!key.StartsWith("S"))
{
return false;
}
if (!key.All(x => b58Chars.Contains(x)))
{
return false;
}
byte[] bytes = Encoding.UTF8.GetBytes(key);
if (HashFunctions.ComputeSha256(bytes.AppendToEnd((byte)'?'))[0] != 0)
{
return false;
}
return true;
}
/// <summary>
/// Returns the 256-bit byte sequence of this instance.
/// </summary>
/// <returns>An array of bytes.</returns>
public byte[] To32Bytes()
{
return HashFunctions.ComputeSha256(bytes);
}
/// <summary>
/// Returns the string representation of this instance.
/// </summary>
/// <returns>The mini key string.</returns>
public override string ToString()
{
return miniKey;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment