# Bycoin 私钥生成规则

# 助记词生成私钥

cn-mnemonic-word-list.txt

en-mnemonic-word-list.txt

1先通过助记词词库获取 12个助记词(可重复)

2将助记词转变成seed

 byte[] data = MnemonicCode.toSeed(words, "");//引用bitcoin库

3.seed通过HMAC512算法生成私钥 如下,key固定传Root,返回的结果就是私钥

 //data是种子 key固定为“Root”
public byte[] hmacSHA512(byte[] data, String key) {
        List<Byte> bytes = new ArrayList<>();
        byte[] dataBytes = data;
        byte[] keybytes = key.getBytes();
        final SecretKeySpec secretKey = new SecretKeySpec(keybytes, "HmacSHA512");
        try {
            Mac mac = Mac.getInstance("HmacSHA512");
            mac.init(secretKey);
            final byte[] macData = mac.doFinal(dataBytes);
            for (int i = 0; i < macData.length; i++) {
                bytes.add((byte) (macData[i] & 0xff));
            }
            byte[] arr = new byte[bytes.size()];
            for (int i = 0; i < bytes.size(); i++) {
                arr[i] = bytes.get(i);
            }
            arr[0] &= 248;
            arr[31] &= 31; // clear top 3 bits
            arr[31] |= 64;
            return arr;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

# 私钥生成公钥

将私钥生成公钥通过Edd25519算法实现,其中Ed25519.scalarMultWithBaseToBytes 方法是Ed25519库直接实现的,原理请查看比原链张同学大佬的文章Ed25519算法

public class DeriveXpub {
    public static byte[] deriveXpub(byte[] xprv) {
        byte[] xpub = new byte[xprv.length];
        byte[] scalar = new byte[xprv.length / 2];
//        for (int i = 0; i < xprv.length / 2; i++) {
//            scalar[i] = xprv[i];
//        }
        System.arraycopy(xprv, 0, scalar, 0, xprv.length / 2);
        byte[] buf = Ed25519.scalarMultWithBaseToBytes(scalar);
//        for (int i = 0; i < buf.length; i++) {
//            xpub[i] = buf[i];
//        }
        System.arraycopy(buf, 0, xpub, 0, buf.length);
//        for (int i = xprv.length / 2; i < xprv.length; i++) {
//            xpub[i] = xprv[i];
//        }
        System.arraycopy(xprv, xprv.length / 2, xpub, xprv.length / 2, xprv.length / 2);
        return xpub;
    }
}

# 签名过程

服务端返回派生规则,然后跟私钥(XprivateKey)根据派生规则进行派生子私钥,核心方法依然是Ed25519

参考比原链沈大佬写的开源库bytom-java-sdk里面有签名的基本实现

以下是基本的实现规则

    
 public EntityManager.submitPaymentEntity createSubmit(BuildUTXOBean.DataBean dataBean, String privatekey,String memo) {
    
        String[][] strings = new String[dataBean.getSigning_instructions().size()][];
        for (int i = 0; i < dataBean.getSigning_instructions().size(); i++) {
            SigningInstructionsBean signingInstructionsBean = dataBean.getSigning_instructions().get(i);
            String sign = null;
            try {
                sign = Hex.toHexString(createSignature(signingInstructionsBean, Hex.decode(privatekey)));
            } catch (Exception e) {
            }
            strings[i] = new String[]{sign};
        }
        submitPaymentEntity.setSignatures(strings);
        return submitPaymentEntity;
    }

public byte[] createSignature(SigningInstructionsBean signingInstructionsBean, byte[] XprivateKey) throws Exception {
        List<String> derivation_path = signingInstructionsBean.getDerivation_path();
        String[] paths = new String[derivation_path.size()];
        for (int i = 0; i < derivation_path.size(); i++) {
            paths[i] = derivation_path.get(i);
        }
        String message = signingInstructionsBean.getSign_data().get(0);
        return createSingature(XprivateKey, paths, message);
    }
    
    //Xprivatekey 跟私钥 paths 派生路径 (服务端返回)message 签名信息(服务端返回)
    
    public byte[] createSingature(byte[] Xprivatekey, String[] paths, String message) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
        byte[] childXprivatekey = createChildXprivatekey(Xprivatekey, paths);
        return Signer.Ed25519InnerSign(createExpandPrivatekey(childXprivatekey), Hex.decode(message));
    }
     //扩展私钥
    public byte[] createExpandPrivatekey(byte[] childXprv) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
        return ExpandedPrivateKey.ExpandedPrivateKey(childXprv);
    }
    //.........

# Keystore实现

# 1.Keystore实现是基于Web3j实现Web3J

implementation 'org.web3j:core:3.3.1-android'

# 2.Bycoin的Keystore在Web3j的Wallet类基础上添加了一些其他字段,type,alias,version,结构如下 其中type version是必须的,alias是当前钱包名称,层级如下

{
    "key_images":{
        "xkeys":[
            {
                "alias":"个人钱包",
                "crypto":{
                    "cipher":"aes-128-ctr",
                    "cipherparams":{
                        "iv":"533d63db72797a5ddb6af2c7a474b4a9"
                    },
                    "ciphertext":"6b1ed192eeb8b48a24494e2443357f599384b2d9f6be149a92f558fae7c914e0cf0aded70fc408ea4ffb2605d5b7b7e3e4f13762ed5e936e4f12a34943333c72",
                    "kdf":"scrypt",
                    "kdfparams":{
                        "dklen":32,
                        "n":4096,
                        "p":6,
                        "r":8,
                        "salt":"f7a928816acc615f13ef515894d9bda576c54854a68cb990719132c77805f30b"
                    },
                    "mac":"3f168be830fdd64d124c4379c506cd8f9ff45308eb953ab3e943b1d03f4ec4dd"
                },
                "type":"bytom_kd",
                "version":1
            }
        ]
    }
}

# 3.解析Keystore

基本逻辑和Web3j里面的类似  区别是Web3j校验Mac采用的是sha3 ,而Bycoin采用的是sha3-256方法

 public static byte[] decrypt(String password, BTMWalletFile walletFile)
            throws CipherException {
        //校验钱包
        BTMWalletUtils.validate(walletFile);
        BTMWalletFile.Crypto crypto = walletFile.getCrypto();
        byte[] mac = Numeric.hexStringToByteArray(crypto.getMac());
        byte[] iv = Numeric.hexStringToByteArray(crypto.getCipherparams().getIv());
        byte[] cipherText = Numeric.hexStringToByteArray(crypto.getCiphertext());
        byte[] derivedKey;
        BTMWalletFile.KdfParams kdfParams = crypto.getKdfparams();
        if (kdfParams instanceof BTMWalletFile.ScryptKdfParams) {
            BTMWalletFile.ScryptKdfParams scryptKdfParams =
                    (BTMWalletFile.ScryptKdfParams) crypto.getKdfparams();
            int dklen = scryptKdfParams.getDklen();
            int n = scryptKdfParams.getN();
            int p = scryptKdfParams.getP();
            int r = scryptKdfParams.getR();
            byte[] salt = Numeric.hexStringToByteArray(scryptKdfParams.getSalt());
            derivedKey = generateDerivedScryptKey(
                    password.getBytes(Charset.forName("UTF-8")), salt, n, r, p, dklen);
        } else if (kdfParams instanceof BTMWalletFile.Aes128CtrKdfParams) {
            BTMWalletFile.Aes128CtrKdfParams aes128CtrKdfParams =
                    (BTMWalletFile.Aes128CtrKdfParams) crypto.getKdfparams();
            int c = aes128CtrKdfParams.getC();
            String prf = aes128CtrKdfParams.getPrf();
            byte[] salt = Numeric.hexStringToByteArray(aes128CtrKdfParams.getSalt());
            derivedKey = BTMWalletUtils.generateAes128CtrDerivedKey(
                    password.getBytes(Charset.forName("UTF-8")), salt, c, prf);
        } else {
            throw new CipherException("Unable to deserialize params: " + crypto.getKdf());
        }
        byte[] derivedMac = null;
        //  bytom 是bytom
        if (walletFile.getType().startsWith("bytom")) {
            //BTM
            derivedMac = generateBTMMac(derivedKey, cipherText);
        } else {
            //ETH
            derivedMac = generateMac(derivedKey, cipherText);
        }
        if (!Arrays.equals(derivedMac, mac)) {
            throw new CipherException("Invalid password provided");
        }
        byte[] encryptKey = Arrays.copyOfRange(derivedKey, 0, 16);
        byte[] privateKey = performCipherOperation(Cipher.DECRYPT_MODE, iv, encryptKey, cipherText);
//        return ECKeyPair.create(privateKey);
        return privateKey;
    }
    //ETH实现
    private static byte[] generateMac(byte[] derivedKey, byte[] cipherText) {
        byte[] result = new byte[16 + cipherText.length];
        System.arraycopy(derivedKey, 16, result, 0, 16);
        System.arraycopy(cipherText, 0, result, 16, cipherText.length);
        return Hash.sha3(result);
    }
    //BTM实现
    public static byte[] generateBTMMac(byte[] derivedKey, byte[] cipherText) {
        byte[] result = new byte[16 + cipherText.length];
        System.arraycopy(derivedKey, 16, result, 0, 16);
        System.arraycopy(cipherText, 0, result, 16, cipherText.length);
        return sha3256(result);
    }
    // SHA3-256 算法
    public static byte[] sha3256(byte[] bytes) {
        Digest digest = new SHA3Digest(256);
        digest.update(bytes, 0, bytes.length);
        byte[] rsData = new byte[digest.getDigestSize()];
        digest.doFinal(rsData, 0);
        return rsData;
    }

# 4.生成Keystore

解析方式和Web3j里面的create是一样的,注意点:生成Mac的方式也是sha3-256。

上次更新: 12/16/2020, 1:49:21 PM