ETH钱包源码分析-创建钱包

ETH钱包源码分析-创建钱包_第1张图片
Android组件化架构

大家好,我系苍王。

以下是我这个系列的相关文章,有兴趣可以参考一下,可以给个喜欢或者关注我的文章。

[[Android]如何做一个崩溃率少于千分之三噶应用app--章节列表]

技术一直都在发展,这个年代应该最热门当属区块链技术,而ETH是区块链发展不可或缺的一环。很多不了解的人都觉得区块链是后端实现的技术,前端没啥参活的事情,其实并不然。
区块链最基础也是最重要的要素——安全,那么涉及到安全,就有一个基础的技术,加密和解密。不远的未来,将会涌现的技术要求就是对加密和解密技术的原理应用的理解,犹如现金对音视频工程师一样渴求。

这节就介绍一个ETH钱包的创建,里面将会涉及一些基础加密的原理。


ETH钱包源码分析-创建钱包_第2张图片
ETH钱包创建流程.png

创建钱包
1.启动钱包加密服务

//跳转到加密服务
Intent generatingService = new Intent(this, WalletGenService.class);
//钱包密码
generatingService.putExtra("PASSWORD", data.getStringExtra("PASSWORD"));
if (data.hasExtra("PRIVATE_KEY"))
        generatingService.putExtra("PRIVATE_KEY", data.getStringExtra("PRIVATE_KEY"));
//启动创建钱包服务
startService(generatingService);

final Handler handler = new Handler();
generateRefreshCount = 0; 
final int walletcount = WalletStorage.getInstance(this).getFullOnly().size();
Runnable runnable = new Runnable() {
      public void run() {
             try {
                 if(walletcount < WalletStorage.getInstance(MainActivity.this).getFullOnly().size()) {
                     //更新钱包列表
                     ((FragmentWallets) fragments[1]).update();
                      return;
                  }
             } catch (Exception e) {
                   e.printStackTrace();
             }
           if (generateRefreshCount++ < 8)
                  handler.postDelayed(this, 3000)
      }
};
handler.postDelayed(runnable, 4000);

WalletGenService是一个IntentService,创建完成后就会结束服务

@Override
    protected void onHandleIntent(Intent intent) {
        //获取密码
        String password = intent.getStringExtra("PASSWORD");
        String privatekey = "";
        //不一定有私钥
        if (intent.hasExtra("PRIVATE_KEY")) {
            normalMode = false;  //如果没有私钥,就是正常创建钱包,有私钥是扫码创建钱包
            privatekey = intent.getStringExtra("PRIVATE_KEY");
        }
        //创建或添加钱包的通知
        sendNotification();
        try {
            String walletAddress;
            if (normalMode) { // 创建新的密钥
                walletAddress = OwnWalletUtils.generateNewWalletFile(password, new File(this.getFilesDir(), ""), true);   //获取钱包地址
            } else { // Privatekey passed  //通过私钥创建,秘钥对
                ECKeyPair keys = ECKeyPair.create(Hex.decode(privatekey));
                walletAddress = OwnWalletUtils.generateWalletFile(password, keys, new File(this.getFilesDir(), ""), true);    //获取钱包地址
            }
           //添加到钱包存储仓库
            WalletStorage.getInstance(this).add(new FullWallet("0x" + walletAddress, walletAddress), this);
            //命名转换
            AddressNameConverter.getInstance(this).put("0x" + walletAddress, "Wallet " + ("0x" + walletAddress).substring(0, 6), this);
            Settings.walletBeingGenerated = false;
            //结束时再次拼接通知
            finished("0x" + walletAddress);
        } catch (CipherException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InvalidAlgorithmParameterException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchProviderException e) {
            e.printStackTrace();
        }
    }

OwnWalletUtils用于钱包的工具类

//创建新钱包
    public static String generateNewWalletFile(
            String password, File destinationDirectory, boolean useFullScrypt)
            throws CipherException, IOException, InvalidAlgorithmParameterException,
            NoSuchAlgorithmException, NoSuchProviderException {
        //创建密钥对
        ECKeyPair ecKeyPair = Keys.createEcKeyPair();
        return generateWalletFile(password, ecKeyPair, destinationDirectory, useFullScrypt);
    }

    public static String generateWalletFile(
            String password, ECKeyPair ecKeyPair, File destinationDirectory, boolean useFullScrypt)
            throws CipherException, IOException {

        WalletFile walletFile; //钱包文件
        if (useFullScrypt) {   //一般使用全加密
            walletFile = Wallet.createStandard(password, ecKeyPair);
        } else {
            walletFile = Wallet.createLight(password, ecKeyPair);
        }

        //获取加密文件名
        String fileName = getWalletFileName(walletFile);
        File destination = new File(destinationDirectory, fileName);
         //将钱包文件放到指定目录
        ObjectMapper objectMapper = ObjectMapperFactory.getObjectMapper();
        objectMapper.writeValue(destination, walletFile);

        return fileName;
    }

Wallet启动标准创建

    public static WalletFile createStandard(String password, ECKeyPair ecKeyPair)
            throws CipherException {
        return create(password, ecKeyPair, N_STANDARD, P_STANDARD);
    }

    //创建钱包文件 n =1<<18,p = 1
    public static WalletFile create(String password, ECKeyPair ecKeyPair, int n, int p)
            throws CipherException {
       //32位随机数
        byte[] salt = generateRandomBytes(32);
        //衍生钥匙,使用了Scrypt加密(sha-256)
        byte[] derivedKey = generateDerivedScryptKey(
                password.getBytes(Charset.forName("UTF-8")), salt, n, R, p, DKLEN);
        //加密钥,截取衍生钥的前16个字符
        byte[] encryptKey = Arrays.copyOfRange(derivedKey, 0, 16);
        //16位随机数
        byte[] iv = generateRandomBytes(16);
        //私钥字符数组
        byte[] privateKeyBytes =
                Numeric.toBytesPadded(ecKeyPair.getPrivateKey(), Keys.PRIVATE_KEY_SIZE);
        //密码文本,使用了AES加密
        byte[] cipherText = performCipherOperation(
                    Cipher.ENCRYPT_MODE, iv, encryptKey, privateKeyBytes);
        //衍生钥和密码文本使用sha-3加密标准Keccak-256 散列算法
        byte[] mac = generateMac(derivedKey, cipherText);

        return createWalletFile(ecKeyPair, cipherText, iv, salt, mac, n, p);
    }

衍生钥使用的是HmacSHA256加密算法,可以使用native 和java来加密,当然native会快一点。

    public static byte[] scrypt(byte[] passwd, byte[] salt, int N, int r, int p, int dkLen) throws GeneralSecurityException {
        //scyyptN使用Native加密,scryptJ使用Java加密
        return native_library_loaded ? scryptN(passwd, salt, N, r, p, dkLen) : scryptJ(passwd, salt, N, r, p, dkLen);
    }

HMAC(散列消息身份验证码: Hashed Message Authentication Code)

它不是散列函数,而是采用散列函数(sha256)与共享密钥一起使用的消息身份验证机制。
可以知道衍生钥是一同用于密码加密,用于身份验证机制。
而传输的密文,使用了AES加密,AES算法用于传递不适合明文传输的报文

    private static byte[] performCipherOperation(
            int mode, byte[] iv, byte[] encryptKey, byte[] text) throws CipherException {

        try {
            IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
            Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");

            SecretKeySpec secretKeySpec = new SecretKeySpec(encryptKey, "AES");
            cipher.init(mode, secretKeySpec, ivParameterSpec);
            return cipher.doFinal(text);
       ……
    }

创建钱包,写入相应的参数,也就是一大寸加密字符

    private static WalletFile createWalletFile(
            ECKeyPair ecKeyPair, byte[] cipherText, byte[] iv, byte[] salt, byte[] mac,
            int n, int p) {
        //新建钱包文件设定地址
        WalletFile walletFile = new WalletFile();
        walletFile.setAddress(Keys.getAddress(ecKeyPair));
       
        //创建加密对象
        WalletFile.Crypto crypto = new WalletFile.Crypto();
        //设置加密 aes-128-ctr
        crypto.setCipher(CIPHER);
        //设置加密描述 ,取两位数的16进制
        crypto.setCiphertext(Numeric.toHexStringNoPrefix(cipherText));
        walletFile.setCrypto(crypto);
        //设置加密参数
        WalletFile.CipherParams cipherParams = new WalletFile.CipherParams();
        cipherParams.setIv(Numeric.toHexStringNoPrefix(iv));
        crypto.setCipherparams(cipherParams);
        //设置kdf参数
        crypto.setKdf(SCRYPT);
        WalletFile.ScryptKdfParams kdfParams = new WalletFile.ScryptKdfParams();
        kdfParams.setDklen(DKLEN);
        kdfParams.setN(n);
        kdfParams.setP(p);
        kdfParams.setR(R);
        kdfParams.setSalt(Numeric.toHexStringNoPrefix(salt));
        crypto.setKdfparams(kdfParams);
          
        crypto.setMac(Numeric.toHexStringNoPrefix(mac));
        walletFile.setCrypto(crypto);
        walletFile.setId(UUID.randomUUID().toString());
        walletFile.setVersion(CURRENT_VERSION);

        return walletFile;
    }

重点说一下公钥私钥对创建,其中使用了椭圆加密算法

    ECKeyPair ecKeyPair = Keys.createEcKeyPair();

    
    public static ECKeyPair createEcKeyPair() throws InvalidAlgorithmParameterException,
            NoSuchAlgorithmException, NoSuchProviderException {
       //椭圆曲线密码创建
        KeyPair keyPair = createSecp256k1KeyPair();
        return ECKeyPair.create(keyPair);
    }

    /**
     * Create a keypair using SECP-256k1 curve.   椭圆密码
     *
     * 

Private keypairs are encoded using PKCS8 公钥密码标注 * *

Private keys are encoded using X.509 数字证书 */ static KeyPair createSecp256k1KeyPair() throws NoSuchProviderException, NoSuchAlgorithmException, InvalidAlgorithmParameterException { //android security框架内置,KeyPairGenerator为抽奖类,返回实体类 KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("ECDSA", "SC"); // ECGenParameterSpec ecGenParameterSpec = new ECGenParameterSpec("secp256k1"); keyPairGenerator.initialize(ecGenParameterSpec, new SecureRandom()); return keyPairGenerator.generateKeyPair(); }

使用了EC的KeyPairGeneratorSpi来初始化参数,并计算出公钥和私钥对,这里就涉及到密码学的椭圆曲线计算了。

public KeyPair generateKeyPair()
        {
            if (!initialised)
            {
                initialize(strength, new SecureRandom());
            }
            //公钥私钥的加密引擎和加密参数
            AsymmetricCipherKeyPair     pair = engine.generateKeyPair();
            ECPublicKeyParameters       pub = (ECPublicKeyParameters)pair.getPublic();
            ECPrivateKeyParameters      priv = (ECPrivateKeyParameters)pair.getPrivate();

            //先计算出公钥
            if (ecParams instanceof ECParameterSpec)
            {
                ECParameterSpec p = (ECParameterSpec)ecParams;

                BCECPublicKey pubKey = new BCECPublicKey(algorithm, pub, p, configuration);
                return new KeyPair(pubKey,
                                   new BCECPrivateKey(algorithm, priv, pubKey, p, configuration));
            }
            else if (ecParams == null)
            {
               return new KeyPair(new BCECPublicKey(algorithm, pub, configuration),
                                   new BCECPrivateKey(algorithm, priv, configuration));
            }
            else
            {
                java.security.spec.ECParameterSpec p = (java.security.spec.ECParameterSpec)ecParams;

                BCECPublicKey pubKey = new BCECPublicKey(algorithm, pub, p, configuration);
                
                //返回公钥私钥对
                return new KeyPair(pubKey, new BCECPrivateKey(algorithm, priv, pubKey, p, configuration));
            }
        }

椭圆曲线加密体制(Elliptic Curve Cryptography,ECC))也是一个基于加法阶数难求问题的密码方案,暂时看来如果参数选择适当,没有量子计算级别的算力是无法攻破的。

ETH钱包源码分析-创建钱包_第3张图片
椭圆曲线图示.png

椭圆曲线算法破解是指允许使用加法来完成。

然后P+Q = R
具体原理只能之后补上了。

下一节将会介绍使用数字货币传输。

群1已满,可以进群2学习组件化

ETH钱包源码分析-创建钱包_第4张图片
组件化2群

你可能感兴趣的:(ETH钱包源码分析-创建钱包)