美文网首页
imtoken 实战化之 助记词校验代码分享

imtoken 实战化之 助记词校验代码分享

作者: 卡哇伊小王子 | 来源:发表于2019-11-29 10:37 被阅读0次

    正常来说,imToken钱包提供三种导入方式,分别是助记词、keystore和私钥。于是我建议他用keystore或者私钥导入。

    没想到这位简友只有助记词导入的方式,而且更致命的是,当初他并没有备份keystore和私钥。

    这就不好玩儿了,我心里暗想他可能与这笔FTN说再见了,因为在加密货币的世界法则里,一旦助记词、keystore和私钥丢失,那这份财产将基本被宣判无法找回。

    这位简友刚刚接触区块链和数字货币,如果钱包丢失的话,很可能会极大地打击到他探索的积极性,我试图想一些其他可能的办法。

    无奈之下,我让他如果信得过我的话,把助记词发给我看一看,因为当时我怀疑他可能弄混了助记词和私钥。

    他很大方,直接发了过来,我看了一下,确实是助记词。那就没办法了,我正想劝他放弃这个钱包,不过眼睛一下子注意到了倒数第二个单词:

    drsign

    印象中这好像不是一个单词,为了确定,我又上词典查了一遍,确实没有这个单词拼写。

    imToken的助记词向来是12个拼写完整且正确的英文单词,不可能出现这样的词汇,唯一的可能就是这位简友当时抄写错了。

    考虑到键盘上“r”与“e”紧挨着,我建议他试试“design”。

    package com.qy.emt.utils.MnemonicUtil;

    import java.util.List;

    public class MnemonicUtil {

    static Stringmessage ="import success";

        public static void validateMnemonics(List mnemonicCodes) {

    try {

    MnemonicCode.INSTANCE.check(mnemonicCodes);

            }catch (org.bitcoinj.crypto.MnemonicException.MnemonicLengthException e) {

    throw new TokenException(Messages.MNEMONIC_INVALID_LENGTH);

            }catch (org.bitcoinj.crypto.MnemonicException.MnemonicWordException e) {

    throw new TokenException(Messages.MNEMONIC_BAD_WORD);

            }catch (Exception e) {

    throw new TokenException(Messages.MNEMONIC_CHECKSUM);

            }

    }

    public static ListrandomMnemonicCodes() {

    return toMnemonicCodes(NumericUtil.generateRandomBytes(16));

        }

    private static ListtoMnemonicCodes(byte[] entropy) {

    try {

    return MnemonicCode.INSTANCE.toMnemonic(entropy);

            }catch (org.bitcoinj.crypto.MnemonicException.MnemonicLengthException e) {

    throw new TokenException(Messages.MNEMONIC_INVALID_LENGTH);

            }catch (Exception e) {

    throw new TokenException(Messages.MNEMONIC_CHECKSUM);

            }

    }

    }

    ----------------------

    package com.qy.emt.utils.MnemonicUtil;

    import com.google.common.base.Strings;

    import java.math.BigInteger;

    import java.nio.ByteBuffer;

    import java.nio.ByteOrder;

    import java.security.SecureRandom;

    import java.util.Arrays;

    import java.util.regex.Pattern;

    public class NumericUtil {

    private final static SecureRandomSECURE_RANDOM =new SecureRandom();

        private static final StringHEX_PREFIX ="0x";

        public static byte[]generateRandomBytes(int size) {

    byte[] bytes =new byte[size];

            SECURE_RANDOM.nextBytes(bytes);

            return bytes;

        }

    public static boolean isValidHex(String value) {

    if (value ==null) {

    return false;

            }

    if (value.startsWith("0x") || value.startsWith("0X")) {

    value = value.substring(2, value.length());

            }

    if (value.length() ==0 || value.length() %2 !=0) {

    return false;

            }

    String pattern ="[0-9a-fA-F]+";

            return Pattern.matches(pattern, value);

            // If TestRpc resolves the following issue, we can reinstate this code

    // https://github.com/ethereumjs/testrpc/issues/220

    // if (value.length() > 3 && value.charAt(2) == '0') {

    //    return false;

    // }

        }

    public static StringcleanHexPrefix(String input) {

    if (hasHexPrefix(input)) {

    return input.substring(2);

            }else {

    return input;

            }

    }

    public static StringprependHexPrefix(String input) {

    if (input.length() >1 && !hasHexPrefix(input)) {

    return HEX_PREFIX + input;

            }else {

    return input;

            }

    }

    private static boolean hasHexPrefix(String input) {

    return input.length() >1 && input.charAt(0) =='0' && input.charAt(1) =='x';

        }

    public static BigIntegerbytesToBigInteger(byte[] value, int offset, int length) {

    return bytesToBigInteger((Arrays.copyOfRange(value, offset, offset + length)));

        }

    public static BigIntegerbytesToBigInteger(byte[] value) {

    return new BigInteger(1, value);

        }

    public static BigIntegerhexToBigInteger(String hexValue) {

    String cleanValue =cleanHexPrefix(hexValue);

            return new BigInteger(cleanValue, 16);

        }

    public static StringbigIntegerToHex(BigInteger value) {

    return value.toString(16);

        }

    public static StringbigIntegerToHexWithZeroPadded(BigInteger value, int size) {

    String result =bigIntegerToHex(value);

            int length = result.length();

            if (length > size) {

    throw new UnsupportedOperationException(

    "Value " + result +"is larger then length " + size);

            }else if (value.signum() <0) {

    throw new UnsupportedOperationException("Value cannot be negative");

            }

    if (length < size) {

    result = Strings.repeat("0", size - length) + result;

            }

    return result;

        }

    public static byte[]bigIntegerToBytesWithZeroPadded(BigInteger value, int length) {

    byte[] result =new byte[length];

            byte[] bytes = value.toByteArray();

            int bytesLength;

            int srcOffset;

            if (bytes[0] ==0) {

    bytesLength = bytes.length -1;

                srcOffset =1;

            }else {

    bytesLength = bytes.length;

                srcOffset =0;

            }

    if (bytesLength > length) {

    throw new RuntimeException("Input is too large to put in byte array of size " + length);

            }

    int destOffset = length - bytesLength;

            System.arraycopy(bytes, srcOffset, result, destOffset, bytesLength);

            return result;

        }

    public static byte[]hexToBytes(String input) {

    String cleanInput =cleanHexPrefix(input);

            int len = cleanInput.length();

            if (len ==0) {

    return new byte[]{};

            }

    byte[] data;

            int startIdx;

            if (len %2 !=0) {

    data =new byte[(len /2) +1];

                data[0] = (byte) Character.digit(cleanInput.charAt(0), 16);

                startIdx =1;

            }else {

    data =new byte[len /2];

                startIdx =0;

            }

    for (int i = startIdx; i < len; i +=2) {

    data[(i +1) /2] = (byte) ((Character.digit(cleanInput.charAt(i), 16) <<4)

    + Character.digit(cleanInput.charAt(i +1), 16));

            }

    return data;

        }

    public static byte[]hexToBytesLittleEndian(String input) {

    byte[] bytes =hexToBytes(input);

            if (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN) {

    return bytes;

            }

    int middle = bytes.length /2;

            for (int i =0; i < middle; i++) {

    byte b = bytes[i];

                bytes[i] = bytes[bytes.length -1 - i];

                bytes[bytes.length -1 - i] = b;

            }

    return bytes;

        }

    public static byte[]reverseBytes(byte[] bytes) {

    int middle = bytes.length /2;

            for (int i =0; i < middle; i++) {

    byte b = bytes[i];

                bytes[i] = bytes[bytes.length -1 - i];

                bytes[bytes.length -1 - i] = b;

            }

    return bytes;

        }

    public static StringbytesToHex(byte[] input) {

    StringBuilder stringBuilder =new StringBuilder();

            if (input.length ==0) {

    return "";

            }

    for (byte anInput : input) {

    stringBuilder.append(String.format("%02x", anInput));

            }

    return stringBuilder.toString();

        }

    public static StringbeBigEndianHex(String hex) {

    if (ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN) {

    return hex;

            }

    return reverseHex(hex);

        }

    public static StringbeLittleEndianHex(String hex) {

    if (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN) {

    return hex;

            }

    return reverseHex(hex);

        }

    private static StringreverseHex(String hex) {

    byte[] bytes =hexToBytes(hex);

            bytes =reverseBytes(bytes);

            return bytesToHex(bytes);

        }

    public static int bytesToInt(byte[] bytes) {

    return ByteBuffer.wrap(bytes).getInt();

        }

    public static byte[]intToBytes(int intValue) {

    byte[] intBytes = ByteBuffer.allocate(4).putInt(intValue).array();

            int zeroLen =0;

            for (byte b : intBytes) {

    if (b !=0) {

    break;

                }

    zeroLen++;

            }

    if (zeroLen ==4) {

    zeroLen =3;

            }

    return Arrays.copyOfRange(intBytes, zeroLen, intBytes.length);

        }

    }

    -------------

    package com.qy.emt.utils.MnemonicUtil;

    import com.google.common.base.Stopwatch;

    import org.bitcoinj.core.Sha256Hash;

    import org.bitcoinj.core.Utils;

    import org.bitcoinj.crypto.MnemonicException;

    import org.bitcoinj.crypto.PBKDF2SHA512;

    import org.slf4j.Logger;

    import org.slf4j.LoggerFactory;

    import java.io.BufferedReader;

    import java.io.FileNotFoundException;

    import java.io.IOException;

    import java.io.InputStream;

    import java.io.InputStreamReader;

    import java.security.MessageDigest;

    import java.util.ArrayList;

    import java.util.Collections;

    import java.util.List;

    import static com.google.common.base.Preconditions.checkNotNull;

    import static org.bitcoinj.core.Utils.HEX;

    public class MnemonicCode {

    private static final Loggerlog = LoggerFactory.getLogger(MnemonicCode.class);

        private ArrayListwordList;

        private static final StringBIP39_ENGLISH_RESOURCE_NAME ="/assets/mnemonic.txt";

        private static final StringBIP39_ENGLISH_SHA256 ="ad90bf3beb7b0eb7e5acd74727dc0da96e0a280a258354e7293fb7e211ac03db";

        /** UNIX time for when the BIP39 standard was finalised. This can be used as a default seed birthday. */

        public static long BIP39_STANDARDISATION_TIME_SECS =1381276800;

        private static final int PBKDF2_ROUNDS =2048;

        public static MnemonicCodeINSTANCE;

        static {

    try {

    INSTANCE =new MnemonicCode();

            }catch (FileNotFoundException e) {

    // We expect failure on Android. The developer has to set INSTANCE themselves.

                if (!Utils.isAndroidRuntime())

    log.error("Could not find word list", e);

            }catch (IOException e) {

    log.error("Failed to load word list", e);

            }

    }

    /** Initialise from the included word list. Won't work on Android. */

        public MnemonicCode()throws IOException {

    this(openDefaultWords(), BIP39_ENGLISH_SHA256);

        }

    private static InputStreamopenDefaultWords()throws IOException {

    InputStream stream = MnemonicCode.class.getResourceAsStream(BIP39_ENGLISH_RESOURCE_NAME);

            if (stream ==null)

    throw new FileNotFoundException(BIP39_ENGLISH_RESOURCE_NAME);

            return stream;

        }

    /**

    * Creates an MnemonicCode object, initializing with words read from the supplied input stream.  If a wordListDigest

    * is supplied the digest of the words will be checked.

    */

        public MnemonicCode(InputStream wordstream, String wordListDigest)throws IOException, IllegalArgumentException {

    BufferedReader br =new BufferedReader(new InputStreamReader(wordstream, "UTF-8"));

            this.wordList =new ArrayList(2048);

            MessageDigest md = Sha256Hash.newDigest();

            String word;

            while ((word = br.readLine()) !=null) {

    md.update(word.getBytes());

                this.wordList.add(word);

            }

    br.close();

            if (this.wordList.size() !=2048)

    throw new IllegalArgumentException("input stream did not contain 2048 words");

            // If a wordListDigest is supplied check to make sure it matches.

            if (wordListDigest !=null) {

    byte[] digest = md.digest();

                String hexdigest =HEX.encode(digest);

                if (!hexdigest.equals(wordListDigest))

    throw new IllegalArgumentException("wordlist digest mismatch");

            }

    }

    /**

    * Gets the word list this code uses.

    */

        public ListgetWordList() {

    return wordList;

        }

    /**

    * Convert mnemonic word list to seed.

    */

        public static byte[]toSeed(List words, String passphrase) {

    checkNotNull(passphrase, "A null passphrase is not allowed.");

            // To create binary seed from mnemonic, we use PBKDF2 function

    // with mnemonic sentence (in UTF-8) used as a password and

    // string "mnemonic" + passphrase (again in UTF-8) used as a

    // salt. Iteration count is set to 4096 and HMAC-SHA512 is

    // used as a pseudo-random function. Desired length of the

    // derived key is 512 bits (= 64 bytes).

    //

            String pass = Utils.join(words);

            String salt ="mnemonic" + passphrase;

            final Stopwatch watch = Stopwatch.createStarted();

            byte[] seed = PBKDF2SHA512.derive(pass, salt, PBKDF2_ROUNDS, 64);

            watch.stop();

            log.info("PBKDF2 took {}", watch);

            return seed;

        }

    /**

    * Convert mnemonic word list to original entropy value.

    */

        public byte[]toEntropy(List words)throws MnemonicException.MnemonicLengthException, MnemonicException.MnemonicWordException, MnemonicException.MnemonicChecksumException {

    if (words.size() %3 >0)

    throw new MnemonicException.MnemonicLengthException("Word list size must be multiple of three words.");

            if (words.size() ==0)

    throw new MnemonicException.MnemonicLengthException("Word list is empty.");

            // Look up all the words in the list and construct the

    // concatenation of the original entropy and the checksum.

    //

            int concatLenBits = words.size() *11;

            boolean[] concatBits =new boolean[concatLenBits];

            int wordindex =0;

            for (String word : words) {

    // Find the words index in the wordlist.

                int ndx = Collections.binarySearch(this.wordList, word);

                if (ndx <0)

    throw new MnemonicException.MnemonicWordException(word);

                // Set the next 11 bits to the value of the index.

                for (int ii =0; ii <11; ++ii)

    concatBits[(wordindex *11) + ii] = (ndx & (1 << (10 - ii))) !=0;

                ++wordindex;

            }

    int checksumLengthBits = concatLenBits /33;

            int entropyLengthBits = concatLenBits - checksumLengthBits;

            // Extract original entropy as bytes.

            byte[] entropy =new byte[entropyLengthBits /8];

            for (int ii =0; ii < entropy.length; ++ii)

    for (int jj =0; jj <8; ++jj)

    if (concatBits[(ii *8) + jj])

    entropy[ii] |=1 << (7 - jj);

            // Take the digest of the entropy.

            byte[] hash = Sha256Hash.hash(entropy);

            boolean[] hashBits = bytesToBits(hash);

            // Check all the checksum bits.

            for (int i =0; i < checksumLengthBits; ++i)

    if (concatBits[entropyLengthBits + i] != hashBits[i])

    throw new MnemonicException.MnemonicChecksumException();

            return entropy;

        }

    /**

    * Convert entropy data to mnemonic word list.

    */

        public List toMnemonic(byte[] entropy)throws MnemonicException.MnemonicLengthException {

    if (entropy.length %4 >0)

    throw new MnemonicException.MnemonicLengthException("Entropy length not multiple of 32 bits.");

            if (entropy.length ==0)

    throw new MnemonicException.MnemonicLengthException("Entropy is empty.");

            // We take initial entropy of ENT bits and compute its

    // checksum by taking first ENT / 32 bits of its SHA256 hash.

            byte[] hash = Sha256Hash.hash(entropy);

            boolean[] hashBits =bytesToBits(hash);

            boolean[] entropyBits =bytesToBits(entropy);

            int checksumLengthBits = entropyBits.length /32;

            // We append these bits to the end of the initial entropy.

            boolean[] concatBits =new boolean[entropyBits.length + checksumLengthBits];

            System.arraycopy(entropyBits, 0, concatBits, 0, entropyBits.length);

            System.arraycopy(hashBits, 0, concatBits, entropyBits.length, checksumLengthBits);

            // Next we take these concatenated bits and split them into

    // groups of 11 bits. Each group encodes number from 0-2047

    // which is a position in a wordlist.  We convert numbers into

    // words and use joined words as mnemonic sentence.

            ArrayList words =new ArrayList();

            int nwords = concatBits.length /11;

            for (int i =0; i < nwords; ++i) {

    int index =0;

                for (int j =0; j <11; ++j) {

    index <<=1;

                    if (concatBits[(i *11) + j])

    index |=0x1;

                }

    words.add(this.wordList.get(index));

            }

    return words;

        }

    /**

    * Check to see if a mnemonic word list is valid.

    */

        public void check(List words)throws MnemonicException {

    toEntropy(words);

        }

    private static boolean[] bytesToBits(byte[] data) {

    boolean[] bits =new boolean[data.length *8];

            for (int i =0; i < data.length; ++i)

    for (int j =0; j <8; ++j)

    bits[(i *8) + j] = (data[i] & (1 << (7 - j))) !=0;

            return bits;

        }

    }

    欢迎线下交流 wx:dwl-1591293009

    相关文章

      网友评论

          本文标题:imtoken 实战化之 助记词校验代码分享

          本文链接:https://www.haomeiwen.com/subject/atmwwctx.html