eu.bittrade.libs.steemj.util.KeyGenerator.java Source code

Java tutorial

Introduction

Here is the source code for eu.bittrade.libs.steemj.util.KeyGenerator.java

Source

/*
 *     This file is part of SteemJ (formerly known as 'Steem-Java-Api-Wrapper')
 * 
 *     SteemJ is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 * 
 *     SteemJ is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 * 
 *     You should have received a copy of the GNU General Public License
 *     along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
 */
package eu.bittrade.libs.steemj.util;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.Security;
import java.util.ArrayList;

import org.apache.commons.lang3.StringUtils;

import eu.bittrade.crypto.core.ECKey;
import eu.bittrade.libs.steemj.configuration.SteemJConfig;
import eu.bittrade.libs.steemj.exceptions.SteemFatalErrorException;
import eu.bittrade.libs.steemj.exceptions.SteemKeyHandlingException;
import eu.bittrade.libs.steemj.protocol.PublicKey;

/**
 * This class can be used to generate a new public, private and brain key. It is
 * more or less the Java implementation of the original graphene implementation
 * that can be found <a href=
 * "https://github.com/cryptonomex/graphene/blob/master/libraries/wallet/wallet.cpp">on
 * GitHub</a>.
 * 
 * @author <a href="http://steemit.com/@dez1337">dez1337</a>
 */
public class KeyGenerator {
    private static final int BRAIN_KEY_WORD_COUNT = 16;

    private String brainKey;
    private ECKey privateKey;

    /**
     * Like {@link #KeyGenerator(String, int) KeyGenerator(String, int)} but
     * automatically suggests a new brain key and uses the sequence 0.
     * 
     * @throws SteemKeyHandlingException
     *             If the an algorithm used by the random generator and to
     *             generate a private key is not supported on your platform.
     */
    public KeyGenerator() throws SteemKeyHandlingException {
        try {
            setBrainKey(suggestBrainKey());
        } catch (SteemKeyHandlingException e) {
            throw new SteemFatalErrorException("The generated brain key was not valid - This should never happen.",
                    e);
        }

        setPrivateKey(this.getBrainKey(), 0);
    }

    /**
     * Create a private and a public key based on the given brain key and the
     * given sequence.
     * 
     * <b>Notice</b> that this method uses the SecureRandom.getInstanceStrong()
     * method to generate random numbers. The algorithm used by this method can
     * be changed by configuring the {@code
     * securerandom.strongAlgorithms} {@link Security} property.
     * 
     * @param brainKey
     *            The brain key used to generate a new key pair.
     * @param sequence
     *            The sequence.
     * @throws SteemKeyHandlingException
     *             If the an algorithm used by the random generator and to
     *             generate a private key is not supported on your platform.
     */
    public KeyGenerator(String brainKey, int sequence) throws SteemKeyHandlingException {
        setBrainKey(brainKey);
        setPrivateKey(this.getBrainKey(), sequence);
    }

    /**
     * Get the generated private key.
     * 
     * @return The generated private key.
     */
    public ECKey getPrivateKey() {
        return privateKey;
    }

    /**
     * Get the uncompressed private key in a WI-Format.
     * 
     * @return The uncompressed private key in a WI-Format.
     */
    public String getPrivateKeyAsWIF() {
        return SteemJUtils.privateKeyToWIF(this.getPrivateKey());
    }

    /**
     * This method implements the Steem "derive_private_key" of the wallet.cpp
     * to generate a new private key based on the given brain key and the given
     * sequence.
     * 
     * @param brainKey
     *            The brain key used to generate a new key pair.
     * @param sequence
     *            The sequence.
     * @throws SteemKeyHandlingException
     *             If the an algorithm used by the random generator and to
     *             generate a private key is not supported on your platform.
     */
    private void setPrivateKey(String brainKey, int sequence) throws SteemKeyHandlingException {
        String brainKeyAndSquence = brainKey + " " + sequence;

        try {
            MessageDigest messageDigest512 = MessageDigest.getInstance("SHA-512");
            MessageDigest messageDigest256 = MessageDigest.getInstance("SHA-256");

            byte[] hashedBrainKeyAndSequence = messageDigest512
                    .digest(brainKeyAndSquence.getBytes(SteemJConfig.getInstance().getEncodingCharset()));
            this.privateKey = ECKey.fromPrivate(messageDigest256.digest(hashedBrainKeyAndSequence));
        } catch (NoSuchAlgorithmException e) {
            throw new SteemKeyHandlingException(
                    "The algorithm used to generate a private key is not supported by your system.", e);
        }
    }

    /**
     * Get the generated public key.
     * 
     * @return The generated public key.
     */
    public PublicKey getPublicKey() {
        return new PublicKey(ECKey.fromPrivate(this.getPrivateKey().getPrivKeyBytes()));
    }

    /**
     * Get the generated brain key.
     * 
     * @return The generated brain key.
     */
    public String getBrainKey() {
        return this.brainKey;
    }

    /**
     * Generate a new brain key.
     * 
     * <b>Notice</b> that this method uses the SecureRandom.getInstanceStrong()
     * method to generate random numbers. The algorithm used by this method can
     * be changed by configuring the {@code
     * securerandom.strongAlgorithms} {@link Security} property.
     * 
     * @return The generated brain key.
     * @throws SteemKeyHandlingException
     *             If the algorithm used by the random generator is not
     *             supported on your platform.
     */
    public static String suggestBrainKey() throws SteemKeyHandlingException {
        ArrayList<String> brainKeyParts = new ArrayList<>();

        try {
            for (int i = 0; i < BRAIN_KEY_WORD_COUNT; i++) {
                brainKeyParts.add(BrainkeyDictionaryManager.getInstance().getBrainKeyDictionary()[SecureRandom
                        .getInstance("SHA1PRNG", "SUN")
                        .nextInt(BrainkeyDictionaryManager.getInstance().getBrainKeyDictionary().length - 1)]
                                .toUpperCase());
            }
        } catch (NoSuchAlgorithmException | NoSuchProviderException e) {
            throw new SteemKeyHandlingException(
                    "The algorithm used to provide a strong random number is not available on your system.", e);
        }

        return StringUtils.join(brainKeyParts, " ");
    }

    /**
     * Verify and set the brain key of this instance.
     * 
     * @param brainKey
     *            The brain key in its String representation.
     * @throws SteemKeyHandlingException
     */
    private void setBrainKey(String brainKey) throws SteemKeyHandlingException {
        if (brainKey.split(" ").length != BRAIN_KEY_WORD_COUNT) {
            throw new SteemKeyHandlingException("The provided brain key has to be a space separated list of "
                    + BRAIN_KEY_WORD_COUNT + " words.");
        }
        this.brainKey = brainKey;
    }
}