美文网首页Blockchain(区块链)
ETH钱包源码分析-创建钱包

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

作者: CangWang | 来源:发表于2018-05-21 10:16 被阅读1465次
    Android组件化架构

    大家好,我系苍王。

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

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

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

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


    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.   椭圆密码
         *
         * <p>Private keypairs are encoded using PKCS8   公钥密码标注
         *
         * <p>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))也是一个基于加法阶数难求问题的密码方案,暂时看来如果参数选择适当,没有量子计算级别的算力是无法攻破的。

    椭圆曲线图示.png

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

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

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

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

    组件化2群

    相关文章

      网友评论

        本文标题:ETH钱包源码分析-创建钱包

        本文链接:https://www.haomeiwen.com/subject/cxxxjftx.html