借助网上的一段描述:
若以银行账户为类比,这 5 个词分别对应内容如下:
地址=银行卡号
密码=银行卡密码
私钥=银行卡号+银行卡密码
助记词=银行卡号+银行卡密码
Keystore+密码=银行卡号+银行卡密码
Keystore ≠ 银行卡号
1 2
|
implementation 'org.web3j:core:3.3.1-android' implementation 'io.github.novacrypto:BIP39:0.1.9'
|
org.web3j:core 这个库是Java的,org.web3j:core:x-android 是兼容Android平台,所有接口和工具类都是为Java应用设计的,所以在Android上使用的时候要注意变通一下。
创建数字身份
创建钱包身份可以通过 WalletUtils 类来实现,它可以创建两种钱包:标准和 BIP39。
可以通过 generateWalletFile 函数创建,直接保存为json文件,以下其他三个函数都是它的封装。
在Android上不建议使用 WalletUtils 的这几个函数创建数字身份。
1 2 3 4
|
WalletUtils.generateFullNewWalletFile(); WalletUtils.generateLightNewWalletFile(); WalletUtils.generateNewWalletFile(); WalletUtils.generateWalletFile();
|
generateFullNewWalletFile 使用N_STANDARD加密强度,在Android上会发送OOM,Android的处理速度也跟不上。
generateLightNewWalletFile 相对来说比较轻量级,但是在我手机(红米4)上也花了21秒才创建完成,而加载为 Credentials 花了40秒。而在一台三星手机跑比较快,7秒左右。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
|
public void (View view) {
try { String password = "123456"; String path = Environment.getExternalStorageDirectory().getPath() + "/MyWallet"; File fileDir = new File(path); if (!fileDir.exists()) { fileDir.mkdirs(); } //生成钱包身份保存到目录下 String fileName = null;
Log.e(TAG, "startClickCreateDefault: "+fileDir.getPath() ); fileName = WalletUtils.generateLightNewWalletFile("123456",fileDir);
Log.e(TAG, "wallet fileName: " + fileName );
//加载钱包身份 Credentials credentials = WalletUtils.loadCredentials(password,path + "/" +fileName); Log.e(TAG, "getAddress: "+credentials.getAddress()); Log.e(TAG, "getPrivateKey: "+credentials.getEcKeyPair().getPrivateKey()); Log.e(TAG, "getPublicKey: "+credentials.getEcKeyPair().getPublicKey()); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (NoSuchProviderException e) { e.printStackTrace(); } catch (InvalidAlgorithmParameterException e) { e.printStackTrace(); } catch (CipherException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
|
创建BIP39身份
可以导出助记词,创建花了43秒,用助记词导入很快,只花了几秒。
1
|
WalletUtils.generateBip39Wallet();
|
WalletUtils提供的这个方法在Android上闪退,只有自己写一个了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
|
public void startClickCreateBip39(View view){
String password = "123456"; String path = Environment.getExternalStorageDirectory().getPath() + "/MyWallet"; File fileDir = new File(path); if (!fileDir.exists()) { fileDir.mkdirs(); } Log.e(TAG, "wallet start"); //闪退 //Bip39Wallet wallet = WalletUtils.generateBip39Wallet(password,fileDir); //String mnemonic = wallet.getMnemonic(); //Log.e(TAG, "助记词wallet.getMnemonic(): " + mnemonic); //String fileName = wallet.getFilename(); //Log.e(TAG, "fileName: " + fileName); try {
StringBuilder sb = new StringBuilder(); byte[] entropy = new byte[Words.TWELVE.byteLength()]; new SecureRandom().nextBytes(entropy); new MnemonicGenerator(English.INSTANCE).createMnemonic(entropy, sb::append); String mnemonic = sb.toString(); //String mnemonic = MnemonicUtils.generateMnemonic(initialEntropy); byte[] seed = MnemonicUtils.generateSeed(mnemonic, password); ECKeyPair privateKey = ECKeyPair.create(sha256(seed));
String fileName = WalletUtils.generateWalletFile(password, privateKey, fileDir, false); Log.e(TAG, "fileName: " + fileName); Log.e(TAG, "助记词wallet.getMnemonic(): " + mnemonic); //加载钱包身份 Credentials credentials = WalletUtils.loadBip39Credentials(password,mnemonic); Log.e(TAG, "getAddress: "+credentials.getAddress()); Log.e(TAG, "getPrivateKey: "+credentials.getEcKeyPair().getPrivateKey()); Log.e(TAG, "getPublicKey: "+credentials.getEcKeyPair().getPublicKey());
} catch (CipherException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (Exception e){ e.printStackTrace(); }
}
|
Android创建钱包
上面这些感觉比较适合Java程序,我们跳进去看看就知道了,其实生成数字身份的代码是:
1 2 3 4
|
public static WalletFile createLight(String password, ECKeyPair ecKeyPair) throws CipherException { return create(password, ecKeyPair, N_LIGHT, P_LIGHT); }
|
针对Android,我们需要将生成的数字身份 WalletFile 转为 JSON (Keystore)保存到 SharedPreferences ,所以整理一个工具类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 大专栏 14、创建/恢复ETH钱包身份s="line">80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
|
import android.support.annotation.Nullable; import android.util.Log;
import org.web3j.crypto.CipherException; import org.web3j.crypto.Credentials; import org.web3j.crypto.ECKeyPair; import org.web3j.crypto.Keys; import org.web3j.crypto.MnemonicUtils; import org.web3j.crypto.Wallet; import org.web3j.crypto.WalletFile;
import java.io.IOException; import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.SecureRandom;
import io.github.novacrypto.bip39.MnemonicGenerator; import io.github.novacrypto.bip39.Words; import io.github.novacrypto.bip39.wordlists.English;
import static org.web3j.crypto.Hash.sha256;
public class MyWalletTool {
private final String TAG = getClass().getName();
/** * 创建一个轻量级钱包,没有助记词 * @param password * @return */ public WalletFile createLightWallet(String password){
WalletFile walletFile = null; try { walletFile = Wallet.createLight(password, Keys.createEcKeyPair()); } catch (CipherException | NoSuchProviderException | NoSuchAlgorithmException | InvalidAlgorithmParameterException e) { e.printStackTrace(); } return walletFile; }
/** * 创建一个助记词 * @return */ public String createMnemonic(){
StringBuilder sb = new StringBuilder(); byte[] entropy = new byte[Words.TWELVE.byteLength()]; new SecureRandom().nextBytes(entropy); new MnemonicGenerator(English.INSTANCE).createMnemonic(entropy, sb::append); return sb.toString(); }
/** * 创建一个带有助记词的轻量级钱包 * @param password * @param mnemonic * @return */
public WalletFile createBip39Wallet(String password,String mnemonic){
WalletFile walletFile = null; try { byte[] seed = MnemonicUtils.generateSeed(mnemonic, password); Log.d(TAG, "createLight start..."); walletFile = Wallet.createLight(password, ECKeyPair.create(sha256(seed))); Log.d(TAG, "createLight end."); } catch (Exception e){ e.printStackTrace(); } return walletFile; }
/** * 生成凭证 * @param password * @param walletFile * @return * @throws CipherException */ public Credentials createCredentials(String password,String mnemonic){
byte[] seed = MnemonicUtils.generateSeed(mnemonic, password); return Credentials.create(ECKeyPair.create(sha256(seed))); }
public Credentials createCredentials(String password,WalletFile walletFile) throws CipherException {
return Credentials.create(Wallet.decrypt(password, walletFile)); }
public Credentials createCredentials(String privateKey) {
return Credentials.create(privateKey); }
|
交易凭证Credentials
在 web3j 中每一个交易都需要一个参数:Credentials,Credentials 实例化有三种方法,其中私钥权限最高,所以绝不能泄露自己的私钥和助记词,常用的是密码 + Keystore。
从MyWalletTool调用的函数来看,交易凭证的实例化只需要以下之一:
- 私钥
- 助记词
- 密码 + Keystore
私钥
一个钱包只有一个私钥且不能修改
为什么 私钥 单独可以实现实例化 Credentials ?
Credentials 的构造函数参数是 ECKeyPair 和 address
1 2 3 4
|
private Credentials(ECKeyPair ecKeyPair, String address) { this.ecKeyPair = ecKeyPair; this.address = address; }
|
address 可以通过 ECKeyPair 推导出来,而 ECKeyPair 的构造函数参数就是公钥和私钥
1 2 3 4
|
public ECKeyPair(BigInteger privateKey, BigInteger publicKey) { this.privateKey = privateKey; this.publicKey = publicKey; }
|
公钥可以通过私钥推导出来,所以可以直接实例化 Credentials。
1
|
Sign.publicKeyFromPrivate(privateKey)
|
助记词
助记词是明文私钥的另一种表现形式,其目的是为了帮助用户记忆复杂的私钥
Canache生成的一个助记词
1 2 3
|
助记词:jump dolphin leave reward allow farm gate hospital region diary seminar loan 地址:0x7E728c371D66813434F340E6D473B212F506bA54 私钥:6229413033912ab1f26e36f0aad7e1ea2b957de73cfedf788b9fff811192aa89
|
用 imToken 可以成功导入钱包,但是用下面的 BIP39 标准的代码却不行(passphrase是加盐,这里为空)。
1 2 3 4
|
byte[] seed = MnemonicUtils.generateSeed(mnemonic, passphrase);//passphrase=null ECKeyPair ecKeyPair = ECKeyPair.create(sha256(seed)); System.out.println("private=" + ecKeyPair.getPrivateKey().toString()); System.out.println("private=" + ecKeyPair.getPrivateKey().toString(16));
|
结果是:
1 2
|
private=27538423023524426157929608133615570842335693203949154557762660148101331275721 private=3ce231f097447fe5d623b3a1f9a37e8c554ee014959903c4e2ebadf69ac7cfc9
|
网上查资料说 imToken 用的是 BIP44 标准。后面再看看怎么搞,imToken核心码开源地址
BIP44助记词创建和导入
Keystore
将私钥以加密的方式保存为一份 JSON 文件,这份 JSON 文件就是 Keystore,所以它就是加密后的私钥,它必须配合钱包密码才能使用该账号。
1
|
ECKeyPair ecKeyPair = Wallet.decrypt(password, walletFile);
|
钱包开源项目
- A ethereum wallet like imToken for Android
- A beautiful, secure and native Ethereum Wallet for Android
- Lightweight JS Wallet for Node and the browser
- A plugin that turns Vault into an Ethereum wallet. Golang
- [币严BIZZAN区块链]Java生成ETH钱包助记词、私钥、地址
本文主要介绍在Java工程中如何生成ETH钱包的助记词.私钥.地址. 一.在之前创建的spring boot 项目中的 pom.xml文件中加入需要的依赖 <dependency> < ...
- EOS智能合约开发(二):EOS创建和管理钱包
上节介绍了EOS智能合约开发之EOS环境搭建及启动节点 那么,节点启动后我们要做的第一件事儿是什么呢?就是我们首先要有账号,但是有账号的前提是什么呢?倒不是先创建账号,而是先要有自己的一组私钥,有了私 ...
- 配置ASM以及创建恢复目录
本次配置ASM沿用了搭建RAC的环境配置,系统选用CENTOS6.8 首先本地配置YUM,安装GRID集群件所需要的RPM包 [root@rac01 Packages]# cd /etc/yum.re ...
- nodejs 使用 ethers创建以太坊钱包
创建钱包创建钱包流程: 生成随机助记词 => 通过助记词创建钱包=>钱包信息和加密明文(私钥和密码加密) 导入钱包通过插件提供方法,根据助记词|keyStore|私钥,找到钱包信息(地址和 ...
- VMware Workstation 14创建mac-10.12虚拟机详细步骤
一.VMware和unlocker的下载和安装 链接:https://pan.baidu.com/s/15Z4DqRENt6JdyfJef_VWSw 密码:40vw 1.安装VMware Works ...
- [Swift通天遁地]九、拔剑吧-(14)创建更美观的景深视差滚动效果
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs. ...
- ABBYY FineReader 14创建PDF文档功能解析
使用ABBYY FineReader,您可以轻松查看和编辑任何类型的 PDF,真的是一款实至名归的PDF编辑转换器,您知道的,它能够保护.签署和编辑PDF文档,甚至还可以创建PDF文档,本文和小编一起 ...
- oracle创建恢复编录(recovery catalog)
1.在要作为恢复编录的数据库创建用户 create user rman identified by oracle default tablespace system temporary TABLESP ...
- 【转载】如何用IntelliJ IDEA 14 创建Web项目
首先要理解一个概念:在IntelliJ IDEA中"new Project"相当于eclipse中的工作空间(Workspace),而"new Module"相 ...
随机推荐
- gcc -S xx
编译器的核心任务是把C程序翻译成机器的汇编语言(assembly language).汇编语言是人类可以阅读的编程语言,也是相当接近实际机器码的语言.由此导致每种 CPU 架构都有不同的汇编语言. 实 ...
- mysql Communications link failure Last packet sent to the server was X ms ago
想必大家在用MySQL时都会遇到连接超时的问题,如下图所示: 就是这个异常(com.mysql.jdbc.exceptions.jdbc4.Communication***ception:Commun ...
- java基本类型和包装类型
int 是基本类型,直接存数值 Integer是类,产生对象时用一个引用指向这个对象 Java把内存划分成两种:一种是栈内存,另一种是堆内存 在函数中定义的一些基本类型的变量和对象的引用变 ...
- spark安装和使用
local模式 概述 local模式就是在一台计算机上运行spark程序,通常用于在本机上练手和测试,它将线程映射为worker. 1)local: 所有计算都运行在一个线程当中,没有任何并行计算,通 ...
- 使用java获取手机号归属地等信息httpClient实现
java获取手机号归属地 一般想获取手机号归属地等信息个人是无法获取的,但是可以通过调用第三方接口获取,具体百度搜索很多这里例子提供一个淘宝的接口 ,该功能已经发布到网站作为一个在线小工具,拿走不谢: ...
- 在VMware装了linux系统,如何在windows系统中用xshell连接
网上有好几种方法,不过我觉得这种比较简单 1.找到VMware菜单 打开 编辑>虚拟网络编辑器 如图: 点下面的更改设置 点确定就可以了,什么都不用改.然后回到linux系统中ifconfig ...
- 腾讯云 Serverless 首发 1ms 计费粒度,立省 70% 费用
云函数 SCF 采用按需付费的方式,并首次发布 1ms 计费粒度,真正实现按使用多少计算能力来计费. 云函数(Serverless Cloud Function,SCF)是腾讯云为企业和开发者们提供的 ...
- day29-struct模块解决黏包问题
#struct模块可以把一个数据类型,例如数字int,转化成固定长度(4个字节)的bytes.int转为4个bytes. #在大量传输数据之前先告诉接收端即将接收数据的大小,方可解决黏包问题: #利用 ...
- linux进程(二)
信号管理进程使用kill命令发送信号与进程通信定义守护进程的角色结束用户会话的进程 kill,killall,pgrep,pkill 对于进程的正常关闭的理解正常关闭程序的方法systemctl st ...
- 1、简述在java网络编程中,服务端程序与客户端程序的具体开发步骤?
网络编程分为UDP通信和TCP通信 UDP协议: 发送端:1.创建DatagramSocket对象.2.创建DatagramPacket对象,并封装数据.3.发送数据.4.释放 资源. 接收端:1.创 ...