简述

ECC(Elliptic Curves Cryptography,椭圆曲线密码编码学)是一种公开密钥算法。基于椭圆曲线数学的公开密钥加密算法,其本质是利用离散对数问题实现加密。
ECC的主要优势,是在使用更小的密钥的同时,提供更快的性能和更高等级的安全。
网上的理论大都讲的非常透彻,我也是看了很多,但是实际能力有限,对数论层面的只停留在浅薄的理解上,不敢乱讲。但是可以简单的说明其原理。

还有一点,加密算法包括RSAECC并不是不可以被破解,只是以当下现代计算机的计算性能算起来比较费劲,理论上破解ECC需要最少250万年,其破解的代价很高,以此来达到不可破解目的。
用量子计算?不说现在有量子技术可不可么,假设量子计算可是可行的,那为什么不升级到量子加密?

ECC: 基于椭圆曲线和离散对数

其原理是数论理论中的单向运算函数,这种函数有一个特点:正方向计算容易,反方向计算却十分困难
啥意思?就是计算:

1234 * 4567 = ?

计算这个简单,结果是:5635678。
那么,返过来计算:

5635678 = x * y

这样就不好计算了,而且结果有很多种有可能是:

5635678 = 1 * 5635678
5635678 = 2 * 2817839
5635678 = 3 * 1408919.5

都有可能。

应用

在编程领域最难的是0到1的过程,而复用前人的技术和经验上就比较轻松。
在对ECC的使用上,已经将这一算法简化到接口层面,通过调用接口来获提需要的安全性。

BouncyCastle 加密工具包

BouncyCastle(轻量级密码术包)是一种用于 Java 平台的开放源码的轻量级密码术包;Bouncycstle 包含了大量的密码算法,其支持椭圆曲线密码算法,并提供JCE 1.2.1的实现。它提供了Java标准库没有的一些算法,例如,RipeMD160哈希算法。

TRON 中也是使用的这个算法工具包。
官网:https://www.bouncycastle.org/

ECKey 类

ECC 类是对加密工具的一个抽象,从类的Copyright上可以看到,这个类实际上是从ethereumJ拿过来的。好的设计都是相通的。

ECC在TRON中,创建账号的时候的用法:

1.获取一个ECKey 对象
2.获得私钥
3.获得公钥

通过 TRON 中生成账户这个接口来,非常典型

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
@Component
@Slf4j(topic = "API")
public class GenerateAddressServlet extends RateLimiterServlet {

protected void doGet(HttpServletRequest request, HttpServletResponse response) {
try {
// 获得一个Sign 接口用来获得私钥
SignInterface sign = SignUtils.getGeneratedRandomSign(Utils.getRandom(),
Args.getInstance().isECKeyCryptoEngine());
// 获得私钥
byte[] priKey = sign.getPrivateKey();
// base58check 地址,我在之前的文章的专门讲过这个格式
byte[] address = sign.getAddress();
// 转成 十六进制字符串,这个也就是通常意义是的私钥,不能丢
String priKeyStr = Hex.encodeHexString(priKey);
// 推出 base58check 地址
String base58check = StringUtil.encode58Check(address);
String hexString = ByteArray.toHexString(address);
JSONObject jsonAddress = new JSONObject();
jsonAddress.put("address", base58check);
jsonAddress.put("hexAddress", hexString);
jsonAddress.put("privateKey", priKeyStr);
// 回写给前端,可以是页面、postman
response.getWriter().println(jsonAddress.toJSONString());
} catch (Exception e) {
Util.processError(e, response);
}
}

protected void doPost(HttpServletRequest request, HttpServletResponse response) {
doGet(request, response);
}
}

调一下试试,反复多调几次可以生成不同的私钥

curl -X GET http://127.0.0.1:8090/wallet/generateaddress

结果关键是拿到私钥,然后通过私钥获得公钥、base58check:

address: TNV7s8K96ZgEiFDuecSXvzKKJgkXwtjkWi
hexAddress: 418949b347588b1901fdf3b6e6dc80dffcd385c4de
privateKey: f7252a484bc631e57910cf65481b12c32b2906fa05742c72f27669b9ddc5d871

使用就是这么简单,可以本地起一个FullNode自行调用接口,产生新的私钥,并没有中心化节点的之间的通信,也可以调用官方节点的接口,都是一样的。可以反复生成私钥,用来测试。

看下SignUtils.getGeneratedRandomSign是怎么处理

1
2
3
4
5
6
7
8
9
public static SignInterface getGeneratedRandomSign(
SecureRandom secureRandom, boolean isECKeyCryptoEngine) {
//安全的随机数,在java中Random是伪随机数,并不安全,SecureRandom 是安全的随机数
// isECKeyCryptoEngine = true
if (isECKeyCryptoEngine) {
return new ECKey(secureRandom);
}
return new SM2(secureRandom);
}

完整示例

使用一个完整的例子来看看怎么用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static void test() {
// 生成一个 ECKey 对象
ECKey ecKey = new ECKey(Utils.getRandom());
String privateKey = ByteArray.toHexString(ecKey.getPrivKeyBytes());
String publicKey = Hex.toHexString(ecKey.getPubKey());
byte[] hexAddress = ecKey.getAddress();
String base58check1 = PublicMethed.getAddressString(privateKey);
String base58check2 = StringUtil.encode58Check(hexAddress);
String bash58check3 = Base58.encode58Check(hexAddress);

System.out.println("private key: " + privateKey);
System.out.println("public key" + publicKey);
System.out.println("hex address1: " + ByteArray.toHexString(hexAddress));
System.out.println("base58check1: " + base58check1);
System.out.println("base58check2: " + base58check2);
System.out.prntln("bash58check3: " + bash58check3);
ByteString bytes = ByteString.copyFrom(Commons.decodeFromBase58Check(base58check1));
System.out.println("hex address2: " + ByteArray.toHexString(bytes.toByteArray()));
}

base58check 地址

base58check是一种特殊的格式,我在之前的文章中讲过个格式。看看它是怎么实现的。
TRON 中有两处需要使用到base58check格式:

  1. 私钥address
  2. 账户address

基本上账户的address用的多,在转账交易中base58check地址用的是最多的。
base58check就是给人看的,实际数据存到数据库中就是byte[]。

看下 base58check 的工作原理:将输入数据进行两次hash后,截取部分数据进行base58编码。

1
2
3
4
5
6
7
8
public static String encode58Check(byte[] input) {
byte[] hash0 = Sha256Hash.hash(CommonParameter.getInstance().isECKeyCryptoEngine(), input);
byte[] hash1 = Sha256Hash.hash(CommonParameter.getInstance().isECKeyCryptoEngine(), hash0);
byte[] inputCheck = new byte[input.length + 4];
System.arraycopy(input, 0, inputCheck, 0, input.length);
System.arraycopy(hash1, 0, inputCheck, input.length, 4);
return Base58.encode(inputCheck);
}

总结

分享了一下ECC的用法,使用场景上不同链的用法也都是大同小异,明白几个概念,在关键处不至于被卡住。
学习方法我不断强调先熟练使用,再谈理解。
总是会有朋友跟我谈如何学习、理解这个问题,总觉得自己不聪明、理解不了。排除每个各人的理解能力,只谈投入时间,如果没有达到称的上努力的程度,请先不要谈悟性,如果你做不到看一眼一分钟内就理解,那么每天最少一小时的时间成本投入都没有,那先不要谈悟性。
时间也是成本,不要吝啬在重要的事情上投入时间