Java tutorial
/* * This file is part of ChangePurse. * * Copyright (c) 2014 Germn Fuentes Capella * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * */ package bit.changepurse.wdk.bip; import static bit.changepurse.wdk.util.CheckedExceptionMethods.generateSecret; import static bit.changepurse.wdk.util.CheckedExceptionMethods.getMessageDigest; import static bit.changepurse.wdk.util.CheckedExceptionMethods.getSecretKeyFactory; import static bit.changepurse.wdk.util.CheckedExceptionMethods.readBits; import java.io.ByteArrayInputStream; import java.security.MessageDigest; import java.security.SecureRandom; import java.text.Normalizer; import javax.crypto.SecretKeyFactory; import javax.crypto.spec.PBEKeySpec; import org.bouncycastle.crypto.PBEParametersGenerator; import org.json.zip.BitInputStream; import bit.changepurse.wdk.util.HashAlgorithm; import bit.changepurse.wdk.util.KeyDerivationAlgorithm; public class MnemonicService implements Bip39 { private final Bip39Dict dict; private SecureRandom rng; public MnemonicService() { this(Bip39Dict.EN); } public MnemonicService(Bip39Dict aDict) { dict = aDict; rng = new SecureRandom(); } @Override public String createMnemonic(EntropyLength entropyLenght) { byte[] data = generateSeedWithChecksum(entropyLenght); BitInputStream is = toBitInputStream(data); String words = dict.getWordList().get(readBits(is, INDEX_SIZE)); for (int i = 1; i < entropyLenght.getNumWords(); i++) { words = addWordToMnemonic(is, words); } return words; } @Override public byte[] extractSeed(String mnemonic, String passphrase) { passphrase = normalizePassphrase(passphrase); mnemonic = normalizeMnemonic(mnemonic); PBEKeySpec spec = createPBESpec(mnemonic, passphrase); SecretKeyFactory skf = getSecretKeyFactory(KeyDerivationAlgorithm.PBKDF2); byte[] hash = generateSecret(skf, spec); return hash; } private PBEKeySpec createPBESpec(String mnemonic, String passphrase) { char[] password = mnemonic.toCharArray(); byte[] salt = PBEParametersGenerator.PKCS5PasswordToUTF8Bytes(passphrase.toCharArray()); PBEKeySpec spec = new PBEKeySpec(password, salt, ITERATION_COUNT, DERIVED_KEY_SIZE); return spec; } private String normalizeMnemonic(String mnemonic) { return normalizeNFKD(mnemonic); } private String normalizePassphrase(String passphrase) { return PASSPHRASE_PREFIX + normalizeMnemonic(passphrase); } private String addWordToMnemonic(BitInputStream is, String words) { int index = readBits(is, INDEX_SIZE); return words + dict.getSpaceString() + dict.getWordList().get(index); } private byte[] generateSeedWithChecksum(EntropyLength entropyLenght) { byte[] seed = generateSeed(entropyLenght); byte[] checksum = generateChecksum(seed); return concatenate(seed, checksum); } private BitInputStream toBitInputStream(byte[] data) { return new BitInputStream(new ByteArrayInputStream(data)); } private String normalizeNFKD(String mnemonic) { return Normalizer.normalize(mnemonic, Normalizer.Form.NFKD); } private byte[] concatenate(byte[] seed, byte[] checksum) { byte[] result = new byte[seed.length + checksum.length]; System.arraycopy(seed, 0, result, 0, seed.length); System.arraycopy(checksum, 0, result, seed.length, checksum.length); return result; } private byte[] generateChecksum(byte[] seed) { MessageDigest md = getMessageDigest(HashAlgorithm.SHA256); md.update(seed); return md.digest(); } private byte[] generateSeed(EntropyLength entropyLenght) { return rng.generateSeed(entropyLenght.toNumBytes()); } public void setRandomNumberGenerator(SecureRandom aRng) { rng = aRng; } }