/// <summary> /// Constructor which attempts to redeem a completed set of three codes, and calculate the private key. /// </summary> public EscrowCodeSet(string code1, string code2, string code3) { if (code1 == null || code2 == null || code3 == null || code1 == "" || code2 == "" || code3 == "") { throw new ArgumentException("Three codes are required to use this function."); } string codea = null, codeb=null, codep = null; if (code1.StartsWith("einva")) codea = code1; if (code2.StartsWith("einva")) codea = code2; if (code3.StartsWith("einva")) codea = code3; if (code1.StartsWith("einvb")) codeb = code1; if (code2.StartsWith("einvb")) codeb = code2; if (code3.StartsWith("einvb")) codeb = code3; if (code1.StartsWith("einvp")) codep = code1; if (code2.StartsWith("einvp")) codep = code2; if (code3.StartsWith("einvp")) codep = code3; if (codea==null || codeb == null || codep == null) { throw new ArgumentException("In order to use this function, one code MUST be an Escrow Invitation A (starting " + "with \"einva\"), one must be an Escrow Invitation B (starting with \"einvb\") and the last " + "code MUST be a Payment Invitation (starting with \"einvp\")."); } byte[] pubparta, privparta; int identifier30a; string failreason = parseEscrowCode(codea, out pubparta, out privparta, out identifier30a); if (failreason != null) throw new ArgumentException("Escrow Invitation Code A: " + failreason); byte[] pubpartb, privpartb; int identifier30b; failreason = parseEscrowCode(codeb, out pubpartb, out privpartb, out identifier30b); if (failreason != null) throw new ArgumentException("Escrow Invitation Code B: " + failreason); if (identifier30a != identifier30b) { throw new ArgumentException("The two Escrow Invitations are not mates and cannot unlock the private key."); } string notvalid = "Not a valid Payment Invitation Code"; string notvalid2 = "Code is not a valid Payment Invitation Code or may have a typo or other error."; string notvalid3 = "The Payment Invitation does not belong to the provided Escrow Invitation."; long headp; byte[] invbytesp; string failReason = parseEitherCode(codep, notvalid, notvalid2, out invbytesp, out headp); if (headp < headbaseP) throw new ArgumentException(notvalid); long identifier30L = headp - headbaseP; if (identifier30L < 0 || identifier30L > 0x3FFFFFFFL) throw new ArgumentException(notvalid); if (identifier30L != (long)identifier30a) { throw new ArgumentException("The Payment Invitation was not generated from either of the provided Escrow Invitation codes and cannot be unlocked by them."); } byte[] privpartz = new byte[32]; Array.Copy(invbytesp, 8 + 1 + 1, privpartz, 0, 32); byte networkByte = invbytesp[8]; bool compressedFlag = (invbytesp[8 + 1 + 1 + 32 + 20] & 0x1) == 1; // get private key BigInteger xyz = new BigInteger(1, privparta).Multiply(new BigInteger(1, privpartb)).Multiply(new BigInteger(1, privpartz)); var ps = Org.BouncyCastle.Asn1.Sec.SecNamedCurves.GetByName("secp256k1"); xyz = xyz.Mod(ps.N); KeyPair kp = new KeyPair(xyz, compressedFlag, networkByte); // provide everything this.EscrowInvitationCodeA = codea; this.EscrowInvitationCodeB = codeb; this.PaymentInvitationCode = codep; this.BitcoinAddress = kp.AddressBase58; this.PrivateKey = kp.PrivateKey; }
/// <summary> Decrypts./ </summary> /// /// auto secret = priv.get_shared_secret(pub); /// auto nonce_plus_secret = fc::sha512::hash(fc::to_string(nonce) + secret.str()); /// auto plain_text = fc::aes_decrypt( nonce_plus_secret, message ); /// auto result = memo_message::deserialize(string(plain_text.begin(), plain_text.end())); /// FC_ASSERT( result.checksum == uint32_t(digest_type::hash(result.text)._hash[0]) ); /// return result.text; /// /// <remarks> Paul, 26/10/2015. </remarks> /// /// <param name="memo"> The memo. </param> /// <param name="receiverPrivateKey"> The receiver private key. </param> /// <param name="senderPublicKey"> The sender public key. </param> /// /// <returns> A string. </returns> public static string Decrypt(GrapheneMemo memo, KeyPair receiverKeyPair) { PublicKey pub = new BitsharesPubKey(memo.from).GetBitcoinPubKey(); byte[] sharedSecret = GetSharedSecret(receiverKeyPair, pub); string ss = StringExtensions.ByteArrayToHexString(sharedSecret, true); string ps = memo.nonce.ToString() + StringExtensions.ByteArrayToHexString(Crypto.ComputeSha512( sharedSecret), true); byte[] seed = ASCIIEncoding.ASCII.GetBytes(ps); string hex = StringExtensions.ByteArrayToHexString(Crypto.ComputeSha512(seed), true); string key = hex.Substring(0, 64); string iv = hex.Substring(64, 32); byte[] data = Util.HexStringToBytes(memo.message); int len = AES.AesDecrypt(data, Util.HexStringToBytes(key), Util.HexStringToBytes(iv)); byte[] result = new byte[len - 4]; Buffer.BlockCopy(data, 4, result, 0, result.Length); string message = UTF8Encoding.UTF8.GetString(result); return message; }
/// <summary> /// Default constructor. Creates a new matched pair of escrow invitation codes. /// </summary> public EscrowCodeSet() { SecureRandom sr = new SecureRandom(); byte[] x = new byte[32]; byte[] y = new byte[32]; sr.NextBytes(x); sr.NextBytes(y); // Force x to be even // Force y to be odd x[31] &= 0xFE; y[31] |= 0x01; KeyPair kx = new KeyPair(x, true); KeyPair ky = new KeyPair(y, true); BigInteger xi = new BigInteger(1, x); BigInteger yi = new BigInteger(1, y); ECPoint Gx = kx.GetECPoint(); byte[] bytesGx = Gx.GetEncoded(); ECPoint Gy = ky.GetECPoint(); byte[] bytesGy = Gy.GetEncoded(); ECPoint Gxy = Gx.Multiply(yi); byte[] bytesGxy = Gxy.GetEncoded(); Sha256Digest sha256 = new Sha256Digest(); byte[] hashGxy = new byte[32]; sha256.BlockUpdate(bytesGxy, 0, bytesGxy.Length); sha256.DoFinal(hashGxy, 0); sha256.BlockUpdate(hashGxy, 0, 32); sha256.DoFinal(hashGxy, 0); int identifier30 = ((hashGxy[0] & 0x3f) << 24) + (hashGxy[1] << 16) + (hashGxy[2] << 8) + hashGxy[3]; byte[] invitationA = new byte[74]; byte[] invitationB = new byte[74]; long headA = headbaseA + (long)identifier30; long headB = headbaseB + (long)identifier30; // turn headA and headB into bytes for (int i = 7; i >= 0; i--) { invitationA[i] = (byte)(headA & 0xFF); invitationB[i] = (byte)(headB & 0xFF); headA >>= 8; headB >>= 8; } Array.Copy(x, 0, invitationA, 8 + 1, 32); Array.Copy(y, 0, invitationB, 8 + 1, 32); Array.Copy(bytesGy, 0, invitationA, 8 + 1 + 32, 33); Array.Copy(bytesGx, 0, invitationB, 8 + 1 + 32, 33); EscrowInvitationCodeA = Util.ByteArrayToBase58Check(invitationA); EscrowInvitationCodeB = Util.ByteArrayToBase58Check(invitationB); }
/// <summary> /// Encryption constructor (non-EC-multiply) /// </summary> public Bip38KeyPair(KeyPair key, string passphrase) { if (passphrase == null && passphrase == "") { throw new ArgumentException("Passphrase is required"); } if (key == null) throw new ArgumentException("Passphrase is required"); this.IsCompressedPoint = key.IsCompressedPoint; this._addressType = key.AddressType; UTF8Encoding utf8 = new UTF8Encoding(false); byte[] addrhashfull = Util.ComputeDoubleSha256(key.AddressBase58); byte[] addresshash = new byte[] { addrhashfull[0], addrhashfull[1], addrhashfull[2], addrhashfull[3] }; byte[] derivedBytes = new byte[64]; SCrypt.ComputeKey(utf8.GetBytes(passphrase), addresshash, 16384, 8, 8, 8, derivedBytes); var aes = Aes.Create(); aes.KeySize = 256; aes.Mode = CipherMode.ECB; byte[] aeskey = new byte[32]; Array.Copy(derivedBytes, 32, aeskey, 0, 32); aes.Key = aeskey; ICryptoTransform encryptor = aes.CreateEncryptor(); byte[] unencrypted = new byte[32]; byte[] rv = new byte[39]; Array.Copy(key.PrivateKeyBytes, unencrypted, 32); for (int x = 0; x < 32; x++) unencrypted[x] ^= derivedBytes[x]; encryptor.TransformBlock(unencrypted, 0, 16, rv, 7); encryptor.TransformBlock(unencrypted, 0, 16, rv, 7); encryptor.TransformBlock(unencrypted, 16, 16, rv, 23); encryptor.TransformBlock(unencrypted, 16, 16, rv, 23); // put header rv[0] = 0x01; rv[1] = 0x42; rv[2] = IsCompressedPoint ? (byte)0xe0 : (byte)0xc0; byte[] checksum = Util.ComputeDoubleSha256(utf8.GetBytes(key.AddressBase58)); rv[3] = checksum[0]; rv[4] = checksum[1]; rv[5] = checksum[2]; rv[6] = checksum[3]; _encryptedKey = Util.ByteArrayToBase58Check(rv); _pubKey = key.PublicKeyBytes; _hash160 = key.Hash160; }
/// <summary> /// Encrypt constructor. /// Creates a new encrypted key pair with a passphrase. /// The resulting key pair record retains the public key and bitcoin address /// but not the passphrase or the unencrypted private key. /// </summary> public ShaPassphraseKeyPair(KeyPair key, string passphrase) { if (passphrase == null || passphrase == "") { throw new ArgumentException("Passphrase is required"); } if (key == null) throw new ArgumentException("Key is required"); IsCompressedPoint = key.IsCompressedPoint; _addressType = key.AddressType; this._hash160 = key.Hash160; this._pubKey = key.PublicKeyBytes; var aes = Aes.Create(); aes.KeySize = 256; aes.Mode = CipherMode.ECB; byte[] encryptionKey = Util.ComputeSha256(passphrase); aes.Key = encryptionKey; ICryptoTransform encryptor = aes.CreateEncryptor(); byte[] rv = new byte[36]; encryptor.TransformBlock(_privKey, 0, 16, rv, 4); encryptor.TransformBlock(_privKey, 0, 16, rv, 4); byte[] interblock = new byte[16]; Array.Copy(rv, 4, interblock, 0, 16); for (int x = 0; x < 16; x++) interblock[x] ^= _privKey[16 + x]; encryptor.TransformBlock(interblock, 0, 16, rv, 20); encryptor.TransformBlock(interblock, 0, 16, rv, 20); // put header rv[0] = 0x02; rv[1] = 0x05; byte[] checksum = Util.ComputeSha256(passphrase + "?"); rv[2] = (byte)(checksum[0] & 0x7F); rv[3] = (byte)(checksum[1] & 0xFE); if (key.IsCompressedPoint) rv[3]++; this._encryptedKey = Util.ByteArrayToBase58Check(rv); }
/// <summary> Gets shared secret. </summary> /// /// def get_shared_secret(priv, pub) : /// pub_point = pub.point() /// priv_point = int(repr(priv),16) /// res = pub_point * priv_point /// res_hex = '%032x' % res.x() /// return res_hex /// /// <remarks> Paul, 26/10/2015. </remarks> /// /// <returns> The shared secret. </returns> public static byte[] GetSharedSecret(KeyPair priv, PublicKey pub) { var ps = SecNamedCurves.GetByName("secp256k1"); BigInteger ipriv = new BigInteger(1, priv.PrivateKeyBytes); ECPoint pubPoint = pub.GetECPoint(); ECPoint shared = pubPoint.Multiply(ipriv); BigInteger s = shared.X.ToBigInteger(); byte[] data = s.ToByteArray(); int leadingZeros=-1; while(data[++leadingZeros] == 0); byte[] result = new byte[data.Length - leadingZeros]; Buffer.BlockCopy(data, leadingZeros, result, 0, result.Length); return result; }
private void computeCode() { // make a compressed key out of it just by using the existing bitcoin address classes KeyPair kp = new KeyPair(_passfactor, compressed: true); _passpoint = kp.PublicKeyBytes; byte[] result = new byte[49]; // 8 bytes are a constant, responsible for making the result start with the characters "passphrase" Array.Copy(magic, 0, result, 0, 8); result[7] = (byte)(LotSequencePresent ? 0x51 : 0x53); Array.Copy(_ownerentropy, 0, result, 8, 8); Array.Copy(_passpoint, 0, result, 16, 33); Code = Util.ByteArrayToBase58Check(result); }
/// <summary> /// A string that allows someone to independently calculate the /// bitcoin address given their key material, without divulging the encrypted private key. /// This can be used to prove that the bitcoin address is encumbered by the passphrase. /// This is null if unavailable or not applicable. When available, calculates upon first /// read, which involves an EC multiply and takes a little bit of CPU time. /// </summary> public string GetConfirmationCode() { if (_confirmationCode != null) return _confirmationCode; if (confirmationCodeInfo == null || factorb == null) return null; // finish calculating the confirmation code // use the KeyPair class to do the EC multiply for us KeyPair kp = new KeyPair(new BigInteger(1, factorb), true); // the public key is 33 bytes, or rather, 32 bytes plus one bit // (the first byte is either 0x02 or 0x03). // xor it with one bit of derived so it's not readable from the confirmation code. byte[] kppubbytes = kp.PublicKeyBytes; confirmationCodeInfo[18] = (byte)(kppubbytes[0] ^ (derived[63] & 0x01)); // xor the pub bytes with derived[0...31] to get similar benefit like having an IV byte[] unencrypted = new byte[32]; for (int i = 0; i < 32; i++) unencrypted[i] = (byte)(kppubbytes[i + 1] ^ derived[i]); // prepare for AES encryption byte[] derivedhalf2 = new byte[32]; Array.Copy(derived, 32, derivedhalf2, 0, 32); var aes = Aes.Create(); aes.KeySize = 256; aes.Mode = CipherMode.ECB; aes.Key = derivedhalf2; ICryptoTransform encryptor = aes.CreateEncryptor(); // encrypt remaining two blocks of 16 bytes using same derived key encryptor.TransformBlock(unencrypted, 0, 16, confirmationCodeInfo, 19); encryptor.TransformBlock(unencrypted, 0, 16, confirmationCodeInfo, 19); encryptor.TransformBlock(unencrypted, 16, 16, confirmationCodeInfo, 19+16); encryptor.TransformBlock(unencrypted, 16, 16, confirmationCodeInfo, 19+16); _confirmationCode = Util.ByteArrayToBase58Check(confirmationCodeInfo); return _confirmationCode; }
public override bool DecryptWithPassphrase(string passphrase) { if (passphrase == null) { return false; } byte[] hex = Util.Base58CheckToByteArray(_encryptedKey); KeyPair tempkey = null; if (hex.Length == 39 && hex[0] == 1 && hex[1] == 0x42) { UTF8Encoding utf8 = new UTF8Encoding(false); byte[] addresshash = new byte[] { hex[3], hex[4], hex[5], hex[6] }; byte[] derivedBytes = new byte[64]; SCrypt.ComputeKey(utf8.GetBytes(passphrase), addresshash, 16384, 8, 8, 8, derivedBytes); var aes = Aes.Create(); aes.KeySize = 256; aes.Mode = CipherMode.ECB; byte[] aeskey = new byte[32]; Array.Copy(derivedBytes, 32, aeskey, 0, 32); aes.Key = aeskey; ICryptoTransform decryptor = aes.CreateDecryptor(); byte[] decrypted = new byte[32]; decryptor.TransformBlock(hex, 7, 16, decrypted, 0); decryptor.TransformBlock(hex, 7, 16, decrypted, 0); decryptor.TransformBlock(hex, 23, 16, decrypted, 16); decryptor.TransformBlock(hex, 23, 16, decrypted, 16); for (int x = 0; x < 32; x++) decrypted[x] ^= derivedBytes[x]; tempkey = new KeyPair(decrypted, compressed: IsCompressedPoint); Sha256Digest sha256 = new Sha256Digest(); byte[] addrhash = new byte[32]; byte[] addrtext = utf8.GetBytes(tempkey.AddressBase58); sha256.BlockUpdate(addrtext, 0, addrtext.Length); sha256.DoFinal(addrhash, 0); sha256.BlockUpdate(addrhash, 0, 32); sha256.DoFinal(addrhash, 0); if (addrhash[0] != hex[3] || addrhash[1] != hex[4] || addrhash[2] != hex[5] || addrhash[3] != hex[6]) { return false; } _privKey = tempkey.PrivateKeyBytes; _pubKey = tempkey.PublicKeyBytes; _hash160 = tempkey.Hash160; return true; } else if (hex.Length == 39 && hex[0] == 1 && hex[1] == 0x43) { // produce the intermediate from the passphrase // get ownersalt and encryptedpart2 since they are in the record byte[] ownersalt = new byte[8]; Array.Copy(hex, 7, ownersalt, 0, 8); bool includeHashStep = (hex[2] & 4) == 4; Bip38Intermediate intermediate = new Bip38Intermediate(passphrase, ownersalt, includeHashStep); this.LotNumber = intermediate.LotNumber; this.SequenceNumber = intermediate.SequenceNumber; tempkey = decryptUsingIntermediate(intermediate, hex); if (verifyAddressHash(tempkey.AddressBase58, hex) == false) return false; } _privKey = tempkey.PrivateKeyBytes; _pubKey = tempkey.PublicKeyBytes; _hash160 = tempkey.Hash160; return true; }
/// <summary> /// Encryption constructor to create a new random key from an intermediate /// </summary> public Bip38KeyPair(Bip38Intermediate intermediate, bool retainPrivateKeyWhenPossible=false) { // generate seedb byte[] seedb = new byte[24]; SecureRandom sr = new SecureRandom(); sr.NextBytes(seedb); // get factorb as sha256(sha256(seedb)) Sha256Digest sha256 = new Sha256Digest(); sha256.BlockUpdate(seedb, 0, 24); factorb = new byte[32]; sha256.DoFinal(factorb, 0); sha256.BlockUpdate(factorb, 0, 32); sha256.DoFinal(factorb, 0); // get ECPoint from passpoint PublicKey pk = new PublicKey(intermediate.passpoint); ECPoint generatedpoint = pk.GetECPoint().Multiply(new BigInteger(1, factorb)); byte[] generatedpointbytes = generatedpoint.GetEncoded(); PublicKey generatedaddress = new PublicKey(generatedpointbytes); // get addresshash UTF8Encoding utf8 = new UTF8Encoding(false); byte[] generatedaddressbytes = utf8.GetBytes(generatedaddress.AddressBase58); sha256.BlockUpdate(generatedaddressbytes, 0, generatedaddressbytes.Length); byte[] addresshashfull = new byte[32]; sha256.DoFinal(addresshashfull, 0); sha256.BlockUpdate(addresshashfull, 0, 32); sha256.DoFinal(addresshashfull, 0); byte[] addresshashplusownerentropy = new byte[12]; Array.Copy(addresshashfull, 0, addresshashplusownerentropy, 0, 4); Array.Copy(intermediate.ownerentropy, 0, addresshashplusownerentropy, 4, 8); // derive encryption key material derived = new byte[64]; SCrypt.ComputeKey(intermediate.passpoint, addresshashplusownerentropy, 1024, 1, 1, 1, derived); byte[] derivedhalf2 = new byte[32]; Array.Copy(derived, 32, derivedhalf2, 0, 32); byte[] unencryptedpart1 = new byte[16]; for (int i = 0; i < 16; i++) { unencryptedpart1[i] = (byte)(seedb[i] ^ derived[i]); } byte[] encryptedpart1 = new byte[16]; // encrypt it var aes = Aes.Create(); aes.KeySize = 256; aes.Mode = CipherMode.ECB; aes.Key = derivedhalf2; ICryptoTransform encryptor = aes.CreateEncryptor(); encryptor.TransformBlock(unencryptedpart1, 0, 16, encryptedpart1, 0); encryptor.TransformBlock(unencryptedpart1, 0, 16, encryptedpart1, 0); byte[] unencryptedpart2 = new byte[16]; for (int i = 0; i < 8; i++) { unencryptedpart2[i] = (byte)(encryptedpart1[i + 8] ^ derived[i + 16]); } for (int i = 0; i < 8; i++) { unencryptedpart2[i + 8] = (byte)(seedb[i + 16] ^ derived[i + 24]); } byte[] encryptedpart2 = new byte[16]; encryptor.TransformBlock(unencryptedpart2, 0, 16, encryptedpart2, 0); encryptor.TransformBlock(unencryptedpart2, 0, 16, encryptedpart2, 0); byte[] result = new byte[39]; result[0] = 0x01; result[1] = 0x43; result[2] = generatedaddress.IsCompressedPoint ? (byte)0x20 : (byte)0x00; if (intermediate.LotSequencePresent) result[2] |= 0x04; Array.Copy(addresshashfull, 0, result, 3, 4); Array.Copy(intermediate.ownerentropy, 0, result, 7, 8); Array.Copy(encryptedpart1, 0, result, 15, 8); Array.Copy(encryptedpart2, 0, result, 23, 16); _encryptedKey = Util.ByteArrayToBase58Check(result); _pubKey = generatedaddress.PublicKeyBytes; _hash160 = generatedaddress.Hash160; var ps = Org.BouncyCastle.Asn1.Sec.SecNamedCurves.GetByName("secp256k1"); if (retainPrivateKeyWhenPossible && intermediate.passfactor != null) { BigInteger privatekey = new BigInteger(1, intermediate.passfactor).Multiply(new BigInteger(1, factorb)).Mod(ps.N); _privKey = new KeyPair(privatekey).PrivateKeyBytes; } // create the confirmation code confirmationCodeInfo = new byte[51]; // constant provides for prefix "cfrm38" confirmationCodeInfo[0] = 0x64; confirmationCodeInfo[1] = 0x3B; confirmationCodeInfo[2] = 0xF6; confirmationCodeInfo[3] = 0xA8; confirmationCodeInfo[4] = 0x9A; // fields for flagbyte, addresshash, and ownerentropy all copy verbatim Array.Copy(result, 2, confirmationCodeInfo, 5, 1 + 4 + 8); }
/// <summary> /// Calculates public key and returns true if calculating public key was possible. /// This might cause a delay of milliseconds if it must be computed from the public /// key, possibly more if it leads to decrypting a private key. /// </summary> protected virtual bool calculatePubKey() { if (IsUnencryptedPrivateKeyAvailable()) { KeyPair kp = new KeyPair(_privKey, compressed: IsCompressedPoint, addressType: _addressType); _pubKey = kp.PublicKeyBytes; return true; } return false; }
protected virtual bool calculateHash160() { if (IsPublicKeyAvailable()) { if (_pubKey == null) { KeyPair kp = new KeyPair(_privKey, this.IsCompressedPoint, this._addressType); _pubKey = kp.PublicKeyBytes; _hash160 = kp.Hash160; } else { PublicKey pub = new PublicKey(_pubKey); _hash160 = pub.Hash160; } return true; } return false; }
public void CheckBitcoinAddresses() { for (int i = 0; i < m_bitcoinPrivKeys.Count; i++) { string priv = m_bitcoinPrivKeys[i]; string addr = m_bitcoinAddresses[i]; KeyPair kp = new KeyPair(priv); Assert.AreEqual(addr, kp.AddressBase58); } }
public static MiniKeyPair CreateDeterministic(string seed) { // flow: // 1. take SHA256 of seed to yield 32 bytes // 2. base58-encode those 32 bytes as though it were a regular private key. now we have 51 characters. // 3. remove all instances of the digit 1. (likely source of typos) // 4. take 29 characters starting with position 4 // (this is to skip those first characters of a base58check-encoded private key with low entropy) // 5. test to see if it matches the typo check. while it does not, increment and try again. UTF8Encoding utf8 = new UTF8Encoding(false); byte[] sha256ofseed = Util.ComputeSha256(seed); string asbase58 = new KeyPair(sha256ofseed).PrivateKeyBase58.Replace("1",""); string keytotry = "S" + asbase58.Substring(4, 29); char[] chars = keytotry.ToCharArray(); char[] charstest = (keytotry + "?").ToCharArray(); while (Util.ComputeSha256(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 MiniKeyPair(new String(chars)); }
/// <summary> /// Generate a set of M-of-N parts for a specific private key. /// If desiredPrivKey is null, then a random key will be selected. /// </summary> public void Generate(int PartsNeededToDecode, int PartsToGenerate, byte[] desiredPrivKey) { if (PartsNeededToDecode > PartsToGenerate) { throw new ApplicationException("Number of parts needed exceeds number of parts to generate."); } if (PartsNeededToDecode > 8 || PartsToGenerate > 8) { throw new ApplicationException("Maximum number of parts is 8"); } if (PartsNeededToDecode < 1 || PartsToGenerate < 1) { throw new ApplicationException("Minimum number of parts is 1"); } if (desiredPrivKey != null && desiredPrivKey.Length != 32) { throw new ApplicationException("Desired private key must be 32 bytes"); } KeyParts.Clear(); decodedKeyParts.Clear(); SecureRandom sr = new SecureRandom(); // Get 8 random big integers into v[i]. byte[][] vvv = new byte[8][]; BigInteger[] v = new BigInteger[8]; for (int i = 0; i < 8; i++) { byte[] b = new byte[32]; sr.NextBytes(b, 0, 32); // For larger values of i, chop off some most-significant-bits to prevent overflows as they are // multiplied with increasingly larger factors. if (i >= 7) { b[0] &= 0x7f; } v[i] = new BigInteger(1, b); Debug.WriteLine(String.Format("v({0})={1}", i, v[i].ToString())); } // if a certain private key is desired, then specify it. if (desiredPrivKey != null) { // replace v[0] with xor(v[1...7]) xor desiredPrivKey BigInteger newv0 = BigInteger.Zero; for (int i=1; i<PartsNeededToDecode; i++) { newv0 = newv0.Xor(v[i]); } v[0] = newv0.Xor(new BigInteger(1,desiredPrivKey)); } // Generate the expected private key from all the parts BigInteger privkey = new BigInteger("0"); for (int i = 0; i < PartsNeededToDecode; i++) { privkey = privkey.Xor(v[i]); } // Get the bitcoin address byte[] keybytes = privkey.ToByteArrayUnsigned(); // make sure we have 32 bytes, we'll need it if (keybytes.Length < 32) { byte[] array32 = new byte[32]; Array.Copy(keybytes, 0, array32, 32 - keybytes.Length, keybytes.Length); keybytes = array32; } KeyPair = new KeyPair(keybytes); byte[] checksum = Util.ComputeSha256(BitcoinAddress); // Generate the parts for (int i = 0; i < PartsToGenerate; i++) { BigInteger total = new BigInteger("0"); for (int j = 0; j < PartsNeededToDecode; j++) { int factor = 1; for (int ii = 0; ii <= i; ii++) factor = factor * (j + 1); BigInteger bfactor = new BigInteger(factor.ToString()); total = total.Add(v[j].Multiply(bfactor)); } Debug.WriteLine(String.Format(" pc{0}={1}", i, total.ToString())); byte[] parts = new byte[39]; parts[0] = 0x4f; parts[1] = (byte)(0x93 + PartsNeededToDecode); int parts23 = (((checksum[0] << 8) + checksum[1]) & 0x1ff); Debug.WriteLine("checksum " + parts23.ToString()); parts23 += 0x6000; parts23 += (i << 9); byte[] btotal = total.ToByteArrayUnsigned(); for (int jj = 0; jj < btotal.Length; jj++) { parts[jj + 4 + (35 - btotal.Length)] = btotal[jj]; } parts[2] = (byte)((parts23 & 0xFF00) >> 8); parts[3] = (byte)(parts23 & 0xFF); KeyParts.Add(Util.ByteArrayToBase58Check(parts)); decodedKeyParts.Add(parts); } }
/// <summary> /// Encryption constructor to create a new random key from an intermediate /// </summary> public Bip38KeyPair(Bip38Intermediate intermediate, bool retainPrivateKeyWhenPossible = false) { // generate seedb byte[] seedb = new byte[24]; SecureRandom sr = new SecureRandom(); sr.NextBytes(seedb); // get factorb as sha256(sha256(seedb)) Sha256Digest sha256 = new Sha256Digest(); sha256.BlockUpdate(seedb, 0, 24); factorb = new byte[32]; sha256.DoFinal(factorb, 0); sha256.BlockUpdate(factorb, 0, 32); sha256.DoFinal(factorb, 0); // get ECPoint from passpoint PublicKey pk = new PublicKey(intermediate.passpoint); ECPoint generatedpoint = pk.GetECPoint().Multiply(new BigInteger(1, factorb)); byte[] generatedpointbytes = generatedpoint.GetEncoded(); PublicKey generatedaddress = new PublicKey(generatedpointbytes); // get addresshash UTF8Encoding utf8 = new UTF8Encoding(false); byte[] generatedaddressbytes = utf8.GetBytes(generatedaddress.AddressBase58); sha256.BlockUpdate(generatedaddressbytes, 0, generatedaddressbytes.Length); byte[] addresshashfull = new byte[32]; sha256.DoFinal(addresshashfull, 0); sha256.BlockUpdate(addresshashfull, 0, 32); sha256.DoFinal(addresshashfull, 0); byte[] addresshashplusownerentropy = new byte[12]; Array.Copy(addresshashfull, 0, addresshashplusownerentropy, 0, 4); Array.Copy(intermediate.ownerentropy, 0, addresshashplusownerentropy, 4, 8); // derive encryption key material derived = new byte[64]; SCrypt.ComputeKey(intermediate.passpoint, addresshashplusownerentropy, 1024, 1, 1, 1, derived); byte[] derivedhalf2 = new byte[32]; Array.Copy(derived, 32, derivedhalf2, 0, 32); byte[] unencryptedpart1 = new byte[16]; for (int i = 0; i < 16; i++) { unencryptedpart1[i] = (byte)(seedb[i] ^ derived[i]); } byte[] encryptedpart1 = new byte[16]; // encrypt it var aes = Aes.Create(); aes.KeySize = 256; aes.Mode = CipherMode.ECB; aes.Key = derivedhalf2; ICryptoTransform encryptor = aes.CreateEncryptor(); encryptor.TransformBlock(unencryptedpart1, 0, 16, encryptedpart1, 0); encryptor.TransformBlock(unencryptedpart1, 0, 16, encryptedpart1, 0); byte[] unencryptedpart2 = new byte[16]; for (int i = 0; i < 8; i++) { unencryptedpart2[i] = (byte)(encryptedpart1[i + 8] ^ derived[i + 16]); } for (int i = 0; i < 8; i++) { unencryptedpart2[i + 8] = (byte)(seedb[i + 16] ^ derived[i + 24]); } byte[] encryptedpart2 = new byte[16]; encryptor.TransformBlock(unencryptedpart2, 0, 16, encryptedpart2, 0); encryptor.TransformBlock(unencryptedpart2, 0, 16, encryptedpart2, 0); byte[] result = new byte[39]; result[0] = 0x01; result[1] = 0x43; result[2] = generatedaddress.IsCompressedPoint ? (byte)0x20 : (byte)0x00; if (intermediate.LotSequencePresent) { result[2] |= 0x04; } Array.Copy(addresshashfull, 0, result, 3, 4); Array.Copy(intermediate.ownerentropy, 0, result, 7, 8); Array.Copy(encryptedpart1, 0, result, 15, 8); Array.Copy(encryptedpart2, 0, result, 23, 16); _encryptedKey = Util.ByteArrayToBase58Check(result); _pubKey = generatedaddress.PublicKeyBytes; _hash160 = generatedaddress.Hash160; var ps = Org.BouncyCastle.Asn1.Sec.SecNamedCurves.GetByName("secp256k1"); if (retainPrivateKeyWhenPossible && intermediate.passfactor != null) { BigInteger privatekey = new BigInteger(1, intermediate.passfactor).Multiply(new BigInteger(1, factorb)).Mod(ps.N); _privKey = new KeyPair(privatekey).PrivateKeyBytes; } // create the confirmation code confirmationCodeInfo = new byte[51]; // constant provides for prefix "cfrm38" confirmationCodeInfo[0] = 0x64; confirmationCodeInfo[1] = 0x3B; confirmationCodeInfo[2] = 0xF6; confirmationCodeInfo[3] = 0xA8; confirmationCodeInfo[4] = 0x9A; // fields for flagbyte, addresshash, and ownerentropy all copy verbatim Array.Copy(result, 2, confirmationCodeInfo, 5, 1 + 4 + 8); }
public override bool DecryptWithPassphrase(string passphrase) { if (passphrase == null) { return(false); } byte[] hex = Util.Base58CheckToByteArray(_encryptedKey); KeyPair tempkey = null; if (hex.Length == 39 && hex[0] == 1 && hex[1] == 0x42) { UTF8Encoding utf8 = new UTF8Encoding(false); byte[] addresshash = new byte[] { hex[3], hex[4], hex[5], hex[6] }; byte[] derivedBytes = new byte[64]; SCrypt.ComputeKey(utf8.GetBytes(passphrase), addresshash, 16384, 8, 8, 8, derivedBytes); var aes = Aes.Create(); aes.KeySize = 256; aes.Mode = CipherMode.ECB; byte[] aeskey = new byte[32]; Array.Copy(derivedBytes, 32, aeskey, 0, 32); aes.Key = aeskey; ICryptoTransform decryptor = aes.CreateDecryptor(); byte[] decrypted = new byte[32]; decryptor.TransformBlock(hex, 7, 16, decrypted, 0); decryptor.TransformBlock(hex, 7, 16, decrypted, 0); decryptor.TransformBlock(hex, 23, 16, decrypted, 16); decryptor.TransformBlock(hex, 23, 16, decrypted, 16); for (int x = 0; x < 32; x++) { decrypted[x] ^= derivedBytes[x]; } tempkey = new KeyPair(decrypted, compressed: IsCompressedPoint); Sha256Digest sha256 = new Sha256Digest(); byte[] addrhash = new byte[32]; byte[] addrtext = utf8.GetBytes(tempkey.AddressBase58); sha256.BlockUpdate(addrtext, 0, addrtext.Length); sha256.DoFinal(addrhash, 0); sha256.BlockUpdate(addrhash, 0, 32); sha256.DoFinal(addrhash, 0); if (addrhash[0] != hex[3] || addrhash[1] != hex[4] || addrhash[2] != hex[5] || addrhash[3] != hex[6]) { return(false); } _privKey = tempkey.PrivateKeyBytes; _pubKey = tempkey.PublicKeyBytes; _hash160 = tempkey.Hash160; return(true); } else if (hex.Length == 39 && hex[0] == 1 && hex[1] == 0x43) { // produce the intermediate from the passphrase // get ownersalt and encryptedpart2 since they are in the record byte[] ownersalt = new byte[8]; Array.Copy(hex, 7, ownersalt, 0, 8); bool includeHashStep = (hex[2] & 4) == 4; Bip38Intermediate intermediate = new Bip38Intermediate(passphrase, ownersalt, includeHashStep); this.LotNumber = intermediate.LotNumber; this.SequenceNumber = intermediate.SequenceNumber; tempkey = decryptUsingIntermediate(intermediate, hex); if (verifyAddressHash(tempkey.AddressBase58, hex) == false) { return(false); } } _privKey = tempkey.PrivateKeyBytes; _pubKey = tempkey.PublicKeyBytes; _hash160 = tempkey.Hash160; return(true); }
private void btnDecrypt_Click(object sender, EventArgs e) { // Remove any spaces or dashes from the encrypted key (in case they were typed) txtEncrypted.Text = txtEncrypted.Text.Replace("-", "").Replace(" ", ""); if (txtEncrypted.Text == "" || txtPassphrase.Text == "") { MessageBox.Show("Enter an encrypted key and its passphrase.", "Entries Required", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } // What were we given as encrypted text? object encrypted = StringInterpreter.Interpret(txtEncrypted.Text); if (encrypted == null) { if (txtEncrypted.Text.StartsWith("cfrm38")) { var r = MessageBox.Show("This is not a private key. This looks like a confirmation code. " + "Do you want to open the Confirmation Code Validator?", "Invalid private key", MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation); if (r == DialogResult.Yes) { Program.ShowConfValidator(); } return; } string containsL = ""; if (txtEncrypted.Text.Contains("l")) { containsL = " Your entry contains the lowercase letter l. Private keys are far " + "more likely to contain the digit 1, and not the lowercase letter l."; } MessageBox.Show("The private key entry (top box) was invalid. " + "Please verify the private key was properly typed." + containsL, "Invalid private key", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); return; } if (encrypted is PassphraseKeyPair) { PassphraseKeyPair pkp = encrypted as PassphraseKeyPair; if (pkp.DecryptWithPassphrase(txtPassphrase.Text) == false) { MessageBox.Show("The passphrase is incorrect.", "Could not decrypt", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); return; } MessageBox.Show("Decryption successful.", "Decryption", MessageBoxButtons.OK, MessageBoxIcon.Information); Program.ShowAddressUtility(); Program.AddressUtility.DisplayKeyCollectionItem(new KeyCollectionItem(pkp.GetUnencryptedPrivateKey())); return; } else if (encrypted is KeyPair) { // it's unencrypted - perhaps we're doing an EC multiply and the passphrase is a private key. object encrypted2 = StringInterpreter.Interpret(txtPassphrase.Text); if (encrypted2 == null) { var r = MessageBox.Show("Does the key you entered belong to the following address?: " + (encrypted as KeyPair).AddressBase58, "Key appears unencrypted", MessageBoxButtons.YesNo); if (r == DialogResult.Yes) { r = MessageBox.Show("Then this key is already unencrypted and you don't need to decrypt it. " + "Would you like to open it in the Address Utility screen to see its various forms?", "Key is not encrypted", MessageBoxButtons.YesNo, MessageBoxIcon.Information); if (r == DialogResult.Yes) { Program.ShowAddressUtility(); Program.AddressUtility.DisplayKeyCollectionItem(new KeyCollectionItem(encrypted as KeyPair)); } } else { MessageBox.Show("The passphrase or secondary key is incorrect. Please verify it was properly typed.", "Second entry is not a valid private key", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); } return; } BigInteger n1 = new BigInteger(1, (encrypted as KeyPair).PrivateKeyBytes); BigInteger n2 = new BigInteger(1, (encrypted2 as KeyPair).PrivateKeyBytes); var ps = Org.BouncyCastle.Asn1.Sec.SecNamedCurves.GetByName("secp256k1"); BigInteger privatekey = n1.Multiply(n2).Mod(ps.N); MessageBox.Show("Keys successfully combined using EC multiplication.", "EC multiplication successful", MessageBoxButtons.OK, MessageBoxIcon.Information); if (n1.Equals(n2)) { MessageBox.Show("The two key entries have the same public hash. The results you see might be wrong.", "Duplicate key hash", MessageBoxButtons.OK, MessageBoxIcon.Information); } // use private key KeyPair kp = new KeyPair(privatekey); Program.ShowAddressUtility(); Program.AddressUtility.DisplayKeyCollectionItem(new KeyCollectionItem(kp)); } else if (encrypted is AddressBase) { MessageBox.Show("This is not a private key. It looks like an address or a public key. Private keys usually start with 5, 6, or S.", "Not a private key", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); } else { MessageBox.Show("This is not a private key that this program can decrypt.", "Not a recognized private key", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); } }
/// <summary> /// Constructor which attempts to redeem a completed set of three codes, and calculate the private key. /// </summary> public EscrowCodeSet(string code1, string code2, string code3) { if (code1 == null || code2 == null || code3 == null || code1 == "" || code2 == "" || code3 == "") { throw new ArgumentException("Three codes are required to use this function."); } string codea = null, codeb = null, codep = null; if (code1.StartsWith("einva")) { codea = code1; } if (code2.StartsWith("einva")) { codea = code2; } if (code3.StartsWith("einva")) { codea = code3; } if (code1.StartsWith("einvb")) { codeb = code1; } if (code2.StartsWith("einvb")) { codeb = code2; } if (code3.StartsWith("einvb")) { codeb = code3; } if (code1.StartsWith("einvp")) { codep = code1; } if (code2.StartsWith("einvp")) { codep = code2; } if (code3.StartsWith("einvp")) { codep = code3; } if (codea == null || codeb == null || codep == null) { throw new ArgumentException("In order to use this function, one code MUST be an Escrow Invitation A (starting " + "with \"einva\"), one must be an Escrow Invitation B (starting with \"einvb\") and the last " + "code MUST be a Payment Invitation (starting with \"einvp\")."); } byte[] pubparta, privparta; int identifier30a; string failreason = parseEscrowCode(codea, out pubparta, out privparta, out identifier30a); if (failreason != null) { throw new ArgumentException("Escrow Invitation Code A: " + failreason); } byte[] pubpartb, privpartb; int identifier30b; failreason = parseEscrowCode(codeb, out pubpartb, out privpartb, out identifier30b); if (failreason != null) { throw new ArgumentException("Escrow Invitation Code B: " + failreason); } if (identifier30a != identifier30b) { throw new ArgumentException("The two Escrow Invitations are not mates and cannot unlock the private key."); } string notvalid = "Not a valid Payment Invitation Code"; string notvalid2 = "Code is not a valid Payment Invitation Code or may have a typo or other error."; // string notvalid3 = "The Payment Invitation does not belong to the provided Escrow Invitation."; long headp; byte[] invbytesp; string failReason = parseEitherCode(codep, notvalid, notvalid2, out invbytesp, out headp); if (headp < headbaseP) { throw new ArgumentException(notvalid); } long identifier30L = headp - headbaseP; if (identifier30L < 0 || identifier30L > 0x3FFFFFFFL) { throw new ArgumentException(notvalid); } if (identifier30L != (long)identifier30a) { throw new ArgumentException("The Payment Invitation was not generated from either of the provided Escrow Invitation codes and cannot be unlocked by them."); } byte[] privpartz = new byte[32]; Array.Copy(invbytesp, 8 + 1 + 1, privpartz, 0, 32); byte networkByte = invbytesp[8]; bool compressedFlag = (invbytesp[8 + 1 + 1 + 32 + 20] & 0x1) == 1; // get private key BigInteger xyz = new BigInteger(1, privparta).Multiply(new BigInteger(1, privpartb)).Multiply(new BigInteger(1, privpartz)); var ps = Org.BouncyCastle.Asn1.Sec.SecNamedCurves.GetByName("secp256k1"); xyz = xyz.Mod(ps.N); KeyPair kp = new KeyPair(xyz, compressedFlag, networkByte); // provide everything this.EscrowInvitationCodeA = codea; this.EscrowInvitationCodeB = codeb; this.PaymentInvitationCode = codep; this.BitcoinAddress = kp.AddressBase58; this.PrivateKey = kp.PrivateKey; }
public BitsharesKeyPair(string wifPrivateKey) { m_btcKeyPair = new KeyPair(wifPrivateKey); m_ripe = System.Security.Cryptography.RIPEMD160.Create(); }
private void setAddressConfirmationCode(int identifier30, byte networkbyte, byte flagbyte, byte[] z, byte[] hash160) { byte[] accbytes = new byte[74]; long head = headconfP + (int)identifier30; for (int i=7; i>=0; i--) { accbytes[i] = (byte)(head & 0x7F); head >>= 8; } accbytes[8]=networkbyte; byte[] Gzbytes = new KeyPair(z, true).GetECPoint().GetEncoded(); Array.Copy(Gzbytes, 0, accbytes, 8+1, 33); Array.Copy(hash160, 0, accbytes, 8+1+33, 20); accbytes[8+1+33+20] = flagbyte; this.AddressConfirmationCode = Util.ByteArrayToBase58Check(accbytes); }
/// <summary> /// Returns true if a given string can be turned into a private key. /// </summary> public static bool IsValidPrivateKey(string key) { KeyPair kp = new KeyPair(); string result = kp.constructWithKey(key, false); return (result == null); }
public void Decode() { ChecksumMatched=false; Decoded=false; KeyPair = null; if (PartsAccepted < PartsNeeded) return; BigInteger[] pc = new BigInteger[8]; for (int i = 0; i < decodedKeyParts.Count; i++) { byte[] g = decodedKeyParts[i]; pc[i] = new BigInteger(1, g, 4, 35); // If there is an overflow, then add it in. if ((g[2] & 0x80) == 0x80) { pc[i] = pc[i].Add(new BigInteger(((int)((g[2] & 0x60) >> 5)).ToString()).ShiftLeft(280)); Debug.WriteLine("overflow added"); } } List<equation> equations = new List<equation>(); // create equations for all the parts we need. for (int i = 0; i < PartsNeeded; i++) { byte[] got = decodedKeyParts[i]; // extract out part number int partnumber0 = (byte)((got[2] & 0x0e) >> 1); equations.Add(new equation(i, PartsNeeded, partnumber0)); } List<List<equation>> steps = new List<List<equation>>(); steps.Add(equations); // goal: get our equation set down such that there's only one coefficient on the left side. while (equations.Count > 1) { equations = solvesome(equations); steps.Add(equations); Debug.WriteLine("-----"); foreach (equation eq in equations) { Debug.WriteLine(eq.ToString()); } } BigInteger[] v = new BigInteger[8]; while (steps.Count > 0) { // pop off the last step List<equation> laststeps = steps[steps.Count - 1]; steps.RemoveAt(steps.Count - 1); equation laststep = laststeps[0]; Debug.WriteLine("-----"); Debug.WriteLine(laststep.ToString()); // solve for v long divisor = laststep.leftside[0].multiplier; laststep.divisor = laststep.leftside[0].multiplier; laststep.leftside[0].multiplier = 1; //foreach (coefficient c in laststep.rightside) c.divisor = divisor; //laststep.subtractor = laststep.subtractor.Divide(new BigInteger(divisor.ToString())); long idx = laststep.leftside[0].vindex; v[idx] = laststep.SolveRight(pc); Debug.WriteLine(String.Format("v({0})={1}", laststep.leftside[0].vindex, v[idx].ToString())); // go through all other steps and see that our solved value is incorporated into the equation. foreach (List<equation> eqbl in steps) { foreach (equation eqb in eqbl) eqb.SolveLeft(v); } } // xor the ones we need BigInteger xoraccum = BigInteger.Zero; for (int i = 0; i < PartsNeeded; i++) { xoraccum = xoraccum.Xor(v[i]); } ChecksumMatched = false; Decoded = true; byte[] keybytes = xoraccum.ToByteArrayUnsigned(); if (keybytes.Length > 32) { // if more than 32 bytes, decoding probably went wrong! truncate to 32 bytes, but force a checksum failure byte[] newkey = new byte[32]; for (int jj = 0; jj < 32; jj++) newkey[jj] = keybytes[jj]; keybytes = newkey; return; } else if (keybytes.Length < 32) { byte[] array32 = new byte[32]; Array.Copy(keybytes, 0, array32, 32 - keybytes.Length, keybytes.Length); keybytes = array32; } KeyPair = new KeyPair(keybytes); // Get the bitcoin address byte[] checksum = Util.ComputeSha256(BitcoinAddress); int mychecksum = ((checksum[0] & 1) << 8) + checksum[1]; if (mychecksum == expectedChecksum) ChecksumMatched=true; }
public void CheckBitcoinPubKeys() { for (int i=0; i<m_bitcoinPrivKeys.Count; i++) { string priv = m_bitcoinPrivKeys[i]; string pub = m_bitcoinPubKeys[i]; KeyPair kp = new KeyPair(priv); Assert.AreEqual( pub, kp.PublicKeyHex.ToLower() ); } }
public void CheckBtsPubKeys() { for (int i = 0; i < m_bitcoinPrivKeys.Count; i++) { string priv = m_bitcoinPrivKeys[i]; string pub = m_btsPubKeys[i]; KeyPair kp = new KeyPair(priv); string compare = BitsharesKeyPair.ComputeBitsharesPubKey(kp.GetCompressed(), m_ripe); Assert.AreEqual(pub, compare); } }
/// <summary> /// flow: /// 1. take SHA256 of seed to yield 32 bytes /// 2. base58-encode those 32 bytes as though it were a regular private key. now we have 51 characters. /// 3. remove all instances of the digit 1. (likely source of typos) /// 4. take 29 characters starting with position 4 /// (this is to skip those first characters of a base58check-encoded private key with low entropy) /// 5. test to see if it matches the typo check. while it does not, increment and try again. /// </summary> /// <returns></returns> public static MiniKeyPair CreateDeterministic(string seed, byte addressType = 0) { UTF8Encoding utf8 = new UTF8Encoding(false); byte[] sha256ofseed = Util.ComputeSha256(seed); string asbase58 = new KeyPair(sha256ofseed).PrivateKeyBase58.Replace("1",""); string keytotry = "S" + asbase58.Substring(4, 29); char[] chars = keytotry.ToCharArray(); char[] charstest = (keytotry + "?").ToCharArray(); while (Util.ComputeSha256(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 MiniKeyPair(new String(chars), addressType); }