以一個(gè)賬戶舉例:
路徑:
m / purpose' / coin_type' / account' / change / address_index
m/44'/60'/0'/0
助記詞:
lonely quote life good erode key benefit cube brass degree nurse april
私鑰:
0x0c09d05b07006d4e7389ad962969eed6c429399ef6608aab92407e85f33d8fb3
公鑰:
0x037549b8496362e87decad41c543e9ee868189fb191ebb4c5cd668b4b251b7b9e2
地址:
0xe1DA06fff8Ca2Dc3bE69276B47501F89112dCF96
以太坊錢包使用戶用來管理以太坊上面的錢包賬戶链患。一個(gè)錢包賬戶有以下信息:私鑰、公鑰和地址瓶您。
其中麻捻,私鑰的另一種形式又有Keystore的形式。
創(chuàng)建賬戶
創(chuàng)建賬號關(guān)鍵是生成一個(gè)私鑰呀袱, 私鑰是一個(gè)32個(gè)字節(jié)的數(shù)贸毕, 生成一個(gè)私鑰在本質(zhì)上在1到之間選一個(gè)數(shù)字。
因此生成密鑰的第一步也是最重要的一步压鉴,是要找到足夠安全的熵源崖咨,即隨機(jī)性來源,只要選取的結(jié)果是不可預(yù)測或不可重復(fù)的油吭,那么選取數(shù)字的具體方法并不重要。
比如可以擲硬幣256次署拟,用紙和筆記錄正反面并轉(zhuǎn)換為0和1婉宰,隨機(jī)得到的256位二進(jìn)制數(shù)字可作為錢包的私鑰。
從編程的角度來看推穷,一般是通過在一個(gè)密碼學(xué)安全的隨機(jī)源(不建議大家自己去寫一個(gè)隨機(jī)數(shù))中取出一長串隨機(jī)字節(jié)心包,對其使用SHA256哈希算法進(jìn)行運(yùn)算,這樣就可以方便地產(chǎn)生一個(gè)256位的數(shù)字馒铃。
形象說法
- 地址=銀行卡號
- 密碼=銀行卡密碼
- 私鑰=銀行卡號+銀行卡密碼
- 助記詞=銀行卡號+銀行卡密碼
- Keystore+密碼=銀行卡號+銀行卡密碼
備份錢包
1蟹腾、既然私鑰、助記詞区宇、Keystore+密碼」如此重要娃殖,那么如何進(jìn)行保存呢,最安全的方法就是:手抄紙上议谷。
2炉爆、由于 Keystore 內(nèi)容較多,手抄不方便,保存在電腦上也不安全芬首,因此可以不對 Keystore 進(jìn)行備份穷蛹,只手抄私鑰唐片、助記詞就足夠了,手抄備份要注意以下幾點(diǎn):
(1)多抄幾份,分別放在不同的安全區(qū)域励烦,并告訴家人。
(2)對手抄內(nèi)容進(jìn)行驗(yàn)證亮隙,導(dǎo)入錢包看能不能成功魔种,防止抄寫錯(cuò)誤。
(3)備份信息不要在聯(lián)網(wǎng)設(shè)備上進(jìn)行傳播封寞。
私鑰
0x0c09d05b07006d4e7389ad962969eed6c429399ef6608aab92407e85f33d8fb3
16進(jìn)制
長度固定:64個(gè)16進(jìn)制數(shù)然评,32個(gè)字節(jié)
隨機(jī)性,2次方種(256位二進(jìn)制數(shù)狈究、32個(gè)字節(jié)數(shù)碗淌、64個(gè)16進(jìn)制數(shù))
總之,私鑰是一個(gè)32個(gè)字節(jié)的數(shù)抖锥, 生成一個(gè)私鑰在本質(zhì)上在1到2之間選一個(gè)數(shù)字,即64位長度的16進(jìn)制字符組成
導(dǎo)出私鑰
創(chuàng)建錢包后亿眠,輸入密碼可以導(dǎo)出私鑰,這個(gè)私鑰屬于明文私鑰磅废,由 64 位字符串組成纳像,一個(gè)錢包只有一個(gè)私鑰且不能修改。
導(dǎo)入私鑰
在導(dǎo)入錢包時(shí)拯勉,輸入私鑰并設(shè)置一個(gè)密碼(不用輸入原密碼)竟趾,就能進(jìn)入錢包并擁有這個(gè)錢包的掌控權(quán),就可以把錢包中的代幣轉(zhuǎn)移走宫峦。
公鑰
1岔帽、長度:40
2、以太坊的公鑰以0x開頭
地址
1导绷、創(chuàng)建錢包后會生成一個(gè)以 0x 開頭的 42 位字符串犀勒,這個(gè)字符串就是錢包地址。也就是20的16進(jìn)制的字符組成妥曲。
2贾费、一個(gè)地址就是一個(gè)錢包賬戶,可以公開檐盟,用于轉(zhuǎn)賬(轉(zhuǎn)入褂萧、轉(zhuǎn)出)和查詢賬戶余額。相當(dāng)于你的銀行卡賬號遵堵。
3箱玷、地址不是公鑰怨规,但是地址是由公鑰生成的,而公鑰又是由私鑰生辰的锡足,這個(gè)過程是不可逆的波丰。
錢包地址形式
以太坊,0x開頭(包括基于以太坊平臺代幣)舶得。形如:
0x0bac871ed2677c4b790065f20bdb20a16fdd9dd3
比特幣:a. 普通地址:1開頭掰烟。b. 隔離見證地址:3開頭
瑞波幣地址:r開頭。
萊特幣地址:L開頭沐批。
keystroe
keystore常見于以太坊錢包纫骑,是你獨(dú)有的、用于簽署交易的以太坊私鑰的加密文件九孩。
keystore是一串Json格式的字符串先馆,可以用任何以太坊錢包打開它。keystore必須配合你的錢包密碼來使用躺彬,備份了keystore同時(shí)別忘了備份錢包的密碼煤墙。
用戶可以使用備份的助記詞,重新導(dǎo)入imToken之類的錢包工具宪拥,用新的密碼生成一個(gè)新的Keystore仿野,可以用這種方法來修改錢包密碼
本質(zhì)是加密后的私鑰。
形式
{
"address": "fa1d10334277d2a8b9115c0369a6511605cd47f5",
"id": "4cea804f-db18-4820-8338-e29c9f9b6f73",
"version": 3,
"crypto": {
"cipher": "aes-128-ctr",
"cipherparams": {
"iv": "9cdf057d21539030ff261d0951eefe10"
},
"ciphertext": "4fa65d6ecd41af51f958c1682a512217b09e26791b63282ba4e104e66f6876a5",
"kdf": "scrypt",
"kdfparams": {
"dklen": 32,
"n": 4096,
"p": 6,
"r": 8,
"salt": "040b2d729fd672e6b2406fc56c0e1e51a3d87cc1aa45199b92863462c5f21634"
},
"mac": "dfb33788f5f336e463bfe348264d31d1db0e8fb24a35d40658db23745ccaab8c"
}
}
備份
錢包里有一個(gè)備份 keystore 功能她君,選擇備份 keystore脚作,輸入密碼,會出現(xiàn)一大段字符缔刹,這個(gè)就是 keystore球涛。
用途
1、Keystore必須配合你的錢包密碼來使用桨螺,如果只是備份了KS宾符,忘記密碼,這個(gè)賬戶也就沒了……
2灭翔、直接使用以太坊錢包,很少會直接看到自己的私鑰辣苏,而是讓你備份Keystore, 配合錢包密碼來使用肝箱。即使黑客獲取了你的Keystore, 還要破解錢包密碼也還是有一定難度的,當(dāng)然錢包密碼也別設(shè)太簡單稀蟋。
導(dǎo)入錢包
1煌张、在導(dǎo)入錢包中,輸入 keystore 和密碼退客,就能進(jìn)入錢包了骏融。
2链嘀、需要說明的是,這個(gè)密碼是本手機(jī)原來設(shè)置的本錢包密碼档玻, 這一點(diǎn)和用私鑰或助記詞導(dǎo)入錢包不一樣怀泊,用私鑰或助記詞導(dǎo)入錢包,不需要知道原密碼误趴,直接重置密碼霹琼。
轉(zhuǎn)賬
助記詞
其實(shí)助記詞是分層確定性錢包 (Hierarchical Deterministic, 我們也稱 HD-Wallet) 特有的私鑰表現(xiàn)形式
1、創(chuàng)建錢包后凉当,會出現(xiàn)一個(gè)備份助記詞功能枣申,選擇備份助記詞,輸入密碼看杭,會出現(xiàn) 12 個(gè)單詞忠藤,每個(gè)單詞之間有一個(gè)空格,這個(gè)就是助記詞楼雹,一個(gè)錢包只有一個(gè)助記詞且不能修改模孩。
2、助記詞是私鑰的另一種表現(xiàn)形式烘豹,具有和私鑰同樣的功能瓜贾,在導(dǎo)入錢包中,輸入助記詞并設(shè)置一個(gè)密碼(不用輸入原密碼)携悯,就能進(jìn)入錢包并擁有這個(gè)錢包的掌控權(quán)祭芦,就可以把錢包中的代幣轉(zhuǎn)移走。
3憔鬼、助記詞是需要配合路徑使用的, 相同的助記詞配合不同的路徑也會生成不同的地址
備份助記詞
一般來說龟劲,錢包不保留助記詞信息,在創(chuàng)建完成后轴或,需要備份助記詞昌跌。不備份助記詞的錢包,是不給用戶使用的照雁。
一旦備份助記詞助記詞只能備份一次蚕愤,備份后,在錢包中再也不會顯示饺蚊,因此在備份時(shí)一定要抄寫下來萍诱。
導(dǎo)入錢包
通過助記詞導(dǎo)入錢包
/**
* 此方式導(dǎo)入到的錢包時(shí)輕錢包
*
* @param walletName
* @param mnemonicCode
* @param password
* @param callback
*/
//導(dǎo)入錢包--助記詞---KS
public static void importWalletByMnemonicCode(String walletName, List<String> mnemonicCode, String password, OnImportMnemonicCallback callback) {
String[] pathArray = ETH_TYPE.split("/");
String passphrase = "";
long creationTimeSeconds = System.currentTimeMillis() / 1000;
DeterministicSeed ds = new DeterministicSeed(mnemonicCode, null, passphrase, creationTimeSeconds);
//種子
byte[] seedBytes = ds.getSeedBytes();
if (seedBytes == null) {
callback.onImportMnemonicFailure(ERROR_CREATE_WALLET_FAILURE);
return;
}
DeterministicKey dkKey = HDKeyDerivation.createMasterPrivateKey(seedBytes);
for (int i = 1; i < pathArray.length; i++) {
ChildNumber childNumber;
if (pathArray[i].endsWith("'")) {
int number = Integer.parseInt(pathArray[i].substring(0,
pathArray[i].length() - 1));
childNumber = new ChildNumber(number, true);
} else {
int number = Integer.parseInt(pathArray[i]);
childNumber = new ChildNumber(number, false);
}
dkKey = HDKeyDerivation.deriveChildKey(dkKey, childNumber);
}
ECKeyPair ecKeyPair = ECKeyPair.create(dkKey.getPrivKeyBytes());
WalletBeanNew walletBean = new WalletBeanNew();
// WalletFile walletFile = Wallet.create(password, ecKeyPair, 1024, 1); // WalletUtils. .generateNewWalletFile();
String privateKey = ecKeyPair.getPrivateKey().toString(16);
String publicKey = ecKeyPair.getPublicKey().toString(16);
walletBean.setPrivateKey(privateKey);
walletBean.setPublic_key(publicKey);
LoggerUtils.i("私鑰: " + privateKey);
try {
String keystore = WalletUtils.generateWalletFile(password, ecKeyPair, new File(MySDK.getKeystoreDir()), false);
keystore = MySDK.getKeystoreDir() + "/" + keystore;
walletBean.setKeystore(keystore);
LoggerUtils.i("錢包keystore: " + keystore);
if (StringUtils.isEmpty(walletName)) {
walletName = generateNewWalletName();
}
walletBean.setName(walletName);
// String addr1 = walletFile.getAddress();
String addr2 = Keys.getAddress(ecKeyPair);
String walletAddress = Keys.toChecksumAddress(addr2);
walletBean.setAddress(walletAddress);//設(shè)置錢包地址
LoggerUtils.i("錢包地址: " + walletAddress);
walletBean.setPassword(password);
StringBuilder sb = new StringBuilder();
for (String mnemonic : mnemonicCode) {
sb.append(mnemonic);
sb.append(" ");
}
LoggerUtils.i("助記詞 === " + sb.toString());
String mnemonicWord = sb.toString();
walletBean.setMnemonic(mnemonicWord);
walletBean.setMIconIndex(getNum(7));
callback.onImportMnemonicSuccess(walletBean);
} catch (CipherException | IOException e) {
callback.onImportMnemonicFailure(e.getMessage());
}
}
通過keystore導(dǎo)入錢包
/**
* 導(dǎo)入錢包--KS
* 此方式導(dǎo)入到的錢包要判斷是不是以太坊標(biāo)準(zhǔn)錢包 使用{@link #loadCredentialsByP(int, String, File)}
*
* @param walletName
* @param password
* @param keystoreContent
* @param callback
*/
public static void importWalletByKeyStore(final String walletName, final String password, final KeystoreBean keystoreBean,String keystoreContent, final OnCreateWalletCallback callback) {
new Thread() {
@Override
public void run() {
super.run();
try {
if (keystoreBean == null) {
MySDK.getHandler().post(() -> callback.onCreateFailure(ERROR_KEYSTORE));
return;
}
final WalletBeanNew bean = new WalletBeanNew();
String path = MySDK.getKeystoreDir() + "/" + getWalletFileName(keystoreBean.getAddress());
File file = new File(path);
if (!file.exists()) {
file.createNewFile();
}
FileOutputStream out = new FileOutputStream(file, false); //如果追加方式用true,此處覆蓋
// StringBuffer sb = new StringBuffer();
byte[] bytes = keystoreContent.getBytes();
out.write(bytes);
// out.write(sb.toString().getBytes("utf-8"));//注意需要轉(zhuǎn)換對應(yīng)的字符集
out.close();
//創(chuàng)建錢包
int p = keystoreBean.getCrypto().getKdfparams().getP();
LoggerUtils.i("p = " + p);
Credentials credentials = loadCredentialsByP(p, password, file);
if (p == 6) {
bean.setLight(true);
} else {
bean.setLight(false);
}
ECKeyPair keyPair = credentials.getEcKeyPair();
String wallet_name = walletName;
if (wallet_name.equals("")) {
//兼容SDK
wallet_name = "new-wallet" + RandomUntil.getSmallLetter(3);
}
bean.setName(wallet_name);
bean.setPrivateKey(keyPair.getPrivateKey().toString(16));//私鑰
bean.setPublic_key(keyPair.getPublicKey().toString(16));//公鑰
bean.setAddress("0x" + Keys.getAddress(keyPair)); //地址
bean.setPassword(password); //密碼
bean.setKeystore(path);
bean.setMIconIndex(getNum(7));
MySDK.getHandler().post(() -> callback.onCreateSuccess(bean));
} catch (IOException | CipherException | NullPointerException | OutOfMemoryError e) {
MySDK.getHandler().post(() -> {
LoggerUtils.e(e.getMessage() + " " + password);
callback.onCreateFailure(e.getMessage());
});
}
}
}.start();
}
通過私鑰導(dǎo)入錢包
/**
* @param walletName 錢包名字
* @param privateKey 錢包私鑰
* @param password 錢包密碼
* @param callback 導(dǎo)入結(jié)果的回調(diào)
*/
//導(dǎo)入錢包--私鑰---產(chǎn)生KS
public static void importPrivateKey(String walletName, final String privateKey, final String password, final OnCreateWalletCallback callback) {
try {
final WalletBeanNew wallet = new WalletBeanNew();
String name = walletName;
if (name.equals("")) {
name = "new-wallet" + RandomUntil.getSmallLetter(3);
}
BigInteger key = new BigInteger(privateKey, 16);
ECKeyPair keyPair = ECKeyPair.create(key);
String private_key = keyPair.getPrivateKey().toString(16);
wallet.setPrivateKey(private_key);
wallet.setPublic_key(keyPair.getPublicKey().toString(16));
wallet.setAddress("0x" + Keys.getAddress(keyPair));
wallet.setName(name);
wallet.setMnemonic("");
wallet.setPassword(password);
String keystore = WalletUtils.generateWalletFile(password, keyPair, new File(MySDK.getKeystoreDir()), false);
keystore = MySDK.getKeystoreDir() + "/" + keystore;
wallet.setKeystore(keystore);
wallet.setMIconIndex(getNum(7));
MySDK.getHandler().post(() -> callback.onCreateSuccess(wallet));
} catch (CipherException | IOException e) {
MySDK.getHandler().post(() -> callback.onCreateFailure(e.getMessage()));
}
}