# Bycoin 私钥生成规则
# 助记词生成私钥
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。