Exemple #1
0
        /// <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;
        }
Exemple #2
0
        /// <summary>
        /// Constructor that calculates the address given one escrow invitation code and one matching payment invitation code.
        /// They can be provided in any order.
        /// </summary>
        public EscrowCodeSet(string code1, string code2)
        {
            if (code1 == null || code2 == null || code1 == "" || code2 == "")
            {
                throw new ArgumentException("Two codes are required to use this function.");
            }

            string escrowInvitationCode = null, paymentInvitationCode = null;

            if (code1.StartsWith("einva") || code1.StartsWith("einvb"))
            {
                escrowInvitationCode = code1;
            }
            if (code2.StartsWith("einva") || code2.StartsWith("einvb"))
            {
                escrowInvitationCode = code2;
            }
            if (code1.StartsWith("einvp"))
            {
                paymentInvitationCode = code1;
            }
            if (code2.StartsWith("einvp"))
            {
                paymentInvitationCode = code2;
            }


            if (escrowInvitationCode == null || paymentInvitationCode == null)
            {
                throw new ArgumentException("In order to use this function, one code MUST be an Escrow Invitation (starting " +
                                            "with \"einva\" or \"einvb\") and the other code MUST be a Payment Invitation (starting with \"einvp\").");
            }

            byte[] pubpart, privpart;
            int    identifier30;
            string failreason = parseEscrowCode(escrowInvitationCode, out pubpart, out privpart, out identifier30);

            if (failreason != null)
            {
                throw new ArgumentException(failreason);
            }


            // Look for first 40 bits being all 0's or all 1's


            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 head;

            byte[] invbytes;
            string failReason = parseEitherCode(paymentInvitationCode, notvalid, notvalid2, out invbytes, out head);

            if (head < headbaseP)
            {
                throw new ArgumentException(notvalid);
            }
            long identifier30L = head - headbaseP;

            if (identifier30L < 0 || identifier30L > 0x3FFFFFFFL)
            {
                throw new ArgumentException(notvalid);
            }

            if ((long)identifier30 != identifier30L)
            {
                throw new ArgumentException(notvalid3);
            }

            byte[] privpartz = new byte[32];
            Array.Copy(invbytes, 8 + 1 + 1, privpartz, 0, 32);
            byte networkByte    = invbytes[8];
            bool compressedFlag = (invbytes[8 + 1 + 1 + 32 + 20] & 0x1) == 1;


            // get bitcoin address
            PublicKey pk   = new PublicKey(pubpart);
            ECPoint   Gxyz = pk.GetECPoint().Multiply(new BigInteger(1, privpart)).Multiply(new BigInteger(1, privpartz));

            // uncompress if compress is not indicated
            if (compressedFlag == false)
            {
                Gxyz = PublicKey.GetUncompressed(Gxyz);
            }

            // We can get the Bitcoin address now, so do so
            PublicKey pkxyz = new PublicKey(Gxyz);

            byte[] addrhash160 = pkxyz.Hash160;
            BitcoinAddress = new AddressBase(addrhash160, networkByte).AddressBase58;

            // Does the hash160 match?
            for (int i = 0; i < 20; i++)
            {
                if (addrhash160[i] != invbytes[8 + 1 + 1 + 32 + i])
                {
                    throw new ArgumentException(notvalid3);
                }
            }

            this.PaymentInvitationCode = paymentInvitationCode;

            byte expectedabflag = (byte)(escrowInvitationCode.StartsWith("einva") ? 2 : 0);

            if ((invbytes[8 + 1 + 1 + 32 + 20] & 0x2) != expectedabflag)
            {
                SamePartyWarningApplies = true;
            }
        }
Exemple #3
0
        /// <summary>
        /// Constructor that takes a single Escrow Invitation Code and produces a Payment Invitation Code.
        /// </summary>
        public EscrowCodeSet(string escrowInvitationCode, bool doCompressed = false, byte networkByte = 0)
        {
            byte[] pubpart, privpart;
            int    identifier30;
            string failreason = parseEscrowCode(escrowInvitationCode, out pubpart, out privpart, out identifier30);

            if (failreason != null)
            {
                throw new ArgumentException(failreason);
            }

            // Look for mismatched parity.
            // (we expect LSB of einva's private part to be 0 and LSB of einvb's private part to be 1)
            // (this is to guarantee that einva's and einvb's private parts aren't equal)
            if ((escrowInvitationCode.StartsWith("einva") && (privpart[31] & 0x01) == 1) ||
                (escrowInvitationCode.StartsWith("einvb") && (privpart[31] & 0x01) == 0))
            {
                throw new ArgumentException("This escrow invitation has mismatched parity.  Ask your escrow agent to " +
                                            "generate a new pair using the latest version of the software.");
            }

            // Look for 48 0's or 48 1's
            if (privpart[0] == privpart[1] && privpart[1] == privpart[2] && privpart[2] == privpart[3] &&
                privpart[3] == privpart[4] && privpart[4] == privpart[5] && privpart[5] == privpart[6] &&
                privpart[6] == privpart[7] && privpart[7] == privpart[8])
            {
                if (privpart[0] == 0x00 || privpart[0] == 0xFF)
                {
                    throw new ArgumentException("This escrow invitation is invalid and cannot be used (bad private key).");
                }
            }


            // produce a new factor
            byte[]       z  = new byte[32];
            SecureRandom sr = new SecureRandom();

            sr.NextBytes(z);

            // calculate Gxy then Gxyz
            PublicKey pk   = new PublicKey(pubpart);
            ECPoint   Gxyz = pk.GetECPoint().Multiply(new BigInteger(1, privpart)).Multiply(new BigInteger(1, z));


            // Uncompress it
            Gxyz = PublicKey.GetUncompressed(Gxyz);

            // We can get the Bitcoin address now, so do so
            PublicKey pkxyz = new PublicKey(Gxyz);

            byte[] hash160 = pkxyz.Hash160;
            BitcoinAddress = new AddressBase(hash160, networkByte).AddressBase58;

            // make the payment invitation record
            byte[] invp  = new byte[74];
            long   headP = headbaseP + (long)identifier30;

            for (int i = 7; i >= 0; i--)
            {
                invp[i] = (byte)(headP & 0xff);
                headP >>= 8;
            }
            invp[8] = networkByte;
            Array.Copy(z, 0, invp, 8 + 1 + 1, 32);

            // set flag to indicate if einvb was used to generate this, and make it available in the object
            if (escrowInvitationCode.StartsWith("einvb"))
            {
                invp[8 + 1 + 1 + 32 + 20] = 0x2;
            }

            // copy hash160
            Array.Copy(hash160, 0, invp, 8 + 1 + 1 + 32, 20);

            PaymentInvitationCode = Util.ByteArrayToBase58Check(invp);
            setAddressConfirmationCode(identifier30, networkByte, invp[8 + 1 + 1 + 32 + 20], z, hash160);
        }
        /// <summary>
        /// Constructor that calculates the address given one escrow invitation code and one matching payment invitation code.
        /// They can be provided in any order.
        /// </summary>
        public EscrowCodeSet(string code1, string code2)
        {
            if (code1 == null || code2 == null || code1 == "" || code2 == "") {
                throw new ArgumentException("Two codes are required to use this function.");
            }

            string escrowInvitationCode=null, paymentInvitationCode=null;

            if (code1.StartsWith("einva") || code1.StartsWith("einvb")) escrowInvitationCode = code1;
            if (code2.StartsWith("einva") || code2.StartsWith("einvb")) escrowInvitationCode = code2;
            if (code1.StartsWith("einvp")) paymentInvitationCode = code1;
            if (code2.StartsWith("einvp")) paymentInvitationCode = code2;

            if (escrowInvitationCode == null || paymentInvitationCode == null) {
                throw new ArgumentException("In order to use this function, one code MUST be an Escrow Invitation (starting " +
                    "with \"einva\" or \"einvb\") and the other code MUST be a Payment Invitation (starting with \"einvp\").");
            }

            byte[] pubpart, privpart;
            int identifier30;
            string failreason = parseEscrowCode(escrowInvitationCode, out pubpart, out privpart, out identifier30);
            if (failreason != null) throw new ArgumentException(failreason);

            // Look for first 40 bits being all 0's or all 1's

            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 head;
            byte[] invbytes;
            string failReason = parseEitherCode(paymentInvitationCode, notvalid, notvalid2, out invbytes, out head);

            if (head < headbaseP) throw new ArgumentException(notvalid);
            long identifier30L = head - headbaseP;
            if (identifier30L < 0 || identifier30L > 0x3FFFFFFFL) throw new ArgumentException(notvalid);

            if ((long)identifier30 != identifier30L) {
                throw new ArgumentException(notvalid3);
            }

            byte[] privpartz = new byte[32];
            Array.Copy(invbytes, 8 + 1 + 1, privpartz, 0, 32);
            byte networkByte = invbytes[8];
            bool compressedFlag = (invbytes[8+1+1+32+20] & 0x1) == 1;

            // get bitcoin address
            PublicKey pk = new PublicKey(pubpart);
            ECPoint Gxyz = pk.GetECPoint().Multiply(new BigInteger(1, privpart)).Multiply(new BigInteger(1, privpartz));
            // uncompress if compress is not indicated
            if (compressedFlag == false) Gxyz = PublicKey.GetUncompressed(Gxyz);

            // We can get the Bitcoin address now, so do so
            PublicKey pkxyz = new PublicKey(Gxyz);
            byte[] addrhash160 = pkxyz.Hash160;
            BitcoinAddress = new AddressBase(addrhash160, networkByte).AddressBase58;

            // Does the hash160 match?
            for (int i = 0; i < 20; i++) {
                if (addrhash160[i] != invbytes[8+1+1+32+i]) {
                    throw new ArgumentException(notvalid3);
                }
            }

            this.PaymentInvitationCode = paymentInvitationCode;

            byte expectedabflag = (byte)(escrowInvitationCode.StartsWith("einva") ? 2 : 0);
            if ((invbytes[8+1+1+32+20] & 0x2) != expectedabflag) {
                SamePartyWarningApplies = true;
            }
        }
        /// <summary>
        /// Constructor that takes a single Escrow Invitation Code and produces a Payment Invitation Code.
        /// </summary>
        public EscrowCodeSet(string escrowInvitationCode, bool doCompressed=false, byte networkByte = 0)
        {
            byte[] pubpart, privpart;
            int identifier30;
            string failreason = parseEscrowCode(escrowInvitationCode, out pubpart, out privpart, out identifier30);
            if (failreason != null) throw new ArgumentException(failreason);

            // Look for mismatched parity.
            // (we expect LSB of einva's private part to be 0 and LSB of einvb's private part to be 1)
            // (this is to guarantee that einva's and einvb's private parts aren't equal)
            if ((escrowInvitationCode.StartsWith("einva") && (privpart[31] & 0x01) == 1) ||
                (escrowInvitationCode.StartsWith("einvb") && (privpart[31] & 0x01) == 0)) {
                throw new ArgumentException("This escrow invitation has mismatched parity.  Ask your escrow agent to " +
                    "generate a new pair using the latest version of the software.");
            }

            // Look for 48 0's or 48 1's
            if (privpart[0] == privpart[1] && privpart[1] == privpart[2] && privpart[2] == privpart[3] &&
               privpart[3] == privpart[4] && privpart[4] == privpart[5] && privpart[5] == privpart[6] &&
               privpart[6] == privpart[7] && privpart[7] == privpart[8]) {
                if (privpart[0] == 0x00 || privpart[0] == 0xFF) {
                    throw new ArgumentException("This escrow invitation is invalid and cannot be used (bad private key).");
                }
            }

            // produce a new factor
            byte[] z = new byte[32];
            SecureRandom sr = new SecureRandom();
            sr.NextBytes(z);

            // calculate Gxy then Gxyz
            PublicKey pk = new PublicKey(pubpart);
            ECPoint Gxyz = pk.GetECPoint().Multiply(new BigInteger(1, privpart)).Multiply(new BigInteger(1, z));

            // Uncompress it
            Gxyz = PublicKey.GetUncompressed(Gxyz);

            // We can get the Bitcoin address now, so do so
            PublicKey pkxyz = new PublicKey(Gxyz);
            byte[] hash160 = pkxyz.Hash160;
            BitcoinAddress = new AddressBase(hash160, networkByte).AddressBase58;

            // make the payment invitation record
            byte[] invp = new byte[74];
            long headP = headbaseP + (long)identifier30;
            for (int i = 7; i >= 0; i--) {
                invp[i] = (byte)(headP & 0xff);
                headP >>= 8;
            }
            invp[8] = networkByte;
            Array.Copy(z, 0, invp, 8 + 1 + 1, 32);

            // set flag to indicate if einvb was used to generate this, and make it available in the object
            if (escrowInvitationCode.StartsWith("einvb")) {
                invp[8 + 1 + 1 + 32 + 20] = 0x2;
            }

            // copy hash160
            Array.Copy(hash160, 0, invp, 8 + 1 + 1 + 32, 20);

            PaymentInvitationCode = Util.ByteArrayToBase58Check(invp);
            setAddressConfirmationCode(identifier30, networkByte, invp[8 + 1 + 1 + 32 + 20], z, hash160);
        }
        /// <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);
        }
Exemple #7
0
        /// <summary>
        /// Constructor that takes a single Escrow Invitation Code and produces a Payment Invitation Code.
        /// </summary>
        public EscrowCodeSet(string escrowInvitationCode, bool doCompressed=false, byte networkByte = 0)
        {
            byte[] pubpart, privpart;
            int identifier30;
            string failreason = parseEscrowCode(escrowInvitationCode, out pubpart, out privpart, out identifier30);
            if (failreason != null) throw new ArgumentException(failreason);

            // produce a new factor
            byte[] z = new byte[32];
            SecureRandom sr = new SecureRandom();
            sr.NextBytes(z);

            // calculate Gxy then Gxyz
            PublicKey pk = new PublicKey(pubpart);
            ECPoint Gxyz = pk.GetECPoint().Multiply(new BigInteger(1, privpart)).Multiply(new BigInteger(1, z));

            // Uncompress it
            Gxyz = PublicKey.GetUncompressed(Gxyz);

            // We can get the Bitcoin address now, so do so
            PublicKey pkxyz = new PublicKey(Gxyz);
            byte[] hash160 = pkxyz.Hash160;
            BitcoinAddress = new AddressBase(hash160, networkByte).AddressBase58;

            // make the payment invitation record
            byte[] invp = new byte[74];
            long headP = headbaseP + (long)identifier30;
            for (int i = 7; i >= 0; i--) {
                invp[i] = (byte)(headP & 0xff);
                headP >>= 8;
            }
            invp[8] = networkByte;
            Array.Copy(z, 0, invp, 8 + 1 + 1, 32);

            // set flag to indicate if einvb was used to generate this, and make it available in the object
            if (escrowInvitationCode.StartsWith("einvb")) {
                invp[8 + 1 + 1 + 32 + 20] = 0x2;
            }

            // copy hash160
            Array.Copy(hash160, 0, invp, 8 + 1 + 1 + 32, 20);

            PaymentInvitationCode = Util.ByteArrayToBase58Check(invp);
            setAddressConfirmationCode(identifier30, networkByte, invp[8 + 1 + 1 + 32 + 20], z, hash160);
        }
        /// <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];
            CryptSharp.Utility.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);
        }