ECC 与 SM2
# 介绍
# ECC
椭圆曲线加密算法原理解析(ECC) (opens new window)
ECC是非对称密码体系中的一个大类(其实总共就两个大类,一个RSA一个ECC),基本原理比较复杂,这里就不展开了,基本涉及的算法有:
- ECDH: 密钥交换算法
- ECIES: 加密算法,IES加密算法的ECC版本
- ECDSA: 签署算法
- EdDSA: 签署算法
- ECMQV: 密钥交换算法
# ECIES
IES(Integrated Encryption Scheme)集成加密方案是一种混合加密方案,可针对能够使用选择明文或选择密文攻击的对手提供语义安全性。该方案的安全性基于计算的Diffie-Hellman问题。指定了IES的两种变体:离散对数集成加密方案和椭圆曲线集成加密方案,也称为椭圆曲线增强加密方案或简称为椭圆曲线加密方案。直到基础组的更改,这两个变体都是相同的。
# ECDH
ECDH是基于ECC和DH的Kx算法。
首先确定曲线E(q)和基点G(注意这是一个数,但是使用大写),这两个的选取过程是通过一定的设计得出的,不同的设计得出的被称为不同的曲线,而后,每个人确定自己的私钥k,并且计算p=k*G。
我们还是用Alice和Bob来分析,对Alice来说,pa=kaG,对Bob来说,pb=kbG,最后双方运算p=kapb=kbpa=kakbG,因此双方得到同样的p。
和DHE类似的,ECDH存在一个动态生成k的算法变形ECDHE。
# ECDSA
ECDSA (opens new window)是基于ECC的签署算法,是一种非确定性算法。具体算法请看wiki,或者进一步找一本ECC密码学的书看。
# EdDSA
EdDSA (opens new window)也是一个基于ECC的签署算法,和ECDSA最大的区别是,EdDSA是一种确定性算法,不需要借助随机数。
# SM2
SM2密码算法使用规范 (opens new window)
SM2算法是我国基于ECC椭圆曲线密码理论自主研发设计,由国家密码管理局于2010年12月17日发布,在密码行业标准GMT 0003.1-2012 SM2 总则中推荐了一条256位曲线作为标准曲线,数字签名算法、密钥交换协议以及公钥加密算法都根据SM2总则选取的有限域和椭圆曲线生成密钥对;在数字签名、密钥交换方面区别于ECDSA、ECDH等国际算法,而是采取了更为安全的机制,提高了计算量和复杂性;在数字签名和验证、消息认证码的生成与验证以及随机数的生成等方面,使用国家密管理局批准的SM3密码杂凑算法和随机数生成器。SM3杂凑算法是我国自主设计的密码杂凑算法,安全性要高于MD5算法(128位)和SHA-1算法(160位),SM3算法的压缩函数与SHA-256具有相似结构,但设计更加复杂;SM4分组密码算法是我国自主设计的分组对称密码算法,与AES算法具有相同的密钥长度128位,在安全性上高于3DES算法,在实际应用中能够抵抗针对分组密码算法的各种攻击方法。
# RSA vs ECC
椭圆曲线密码学是一种基于椭圆曲线数学的公开密钥加密算法。 ECC的主要优势是它相比RSA加密算法使用较小的密钥长度并提供相当等级的安全性。ECC的另一个优势是可以定义群之间的双线性映射,基于Weil对或是Tate对;双线性映射已经在密码学中发现了大量的应用,例如基于身份的加密。
# 代码实现
# 加密三方库
https://www.bouncycastle.org/ (opens new window)
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.70</version>
</dependency>
2
3
4
5
# ECC 实践
加密算法: ECIES
签名算法:ECDSA
package com.unclezs.samples.crypto;
import com.sun.org.apache.xerces.internal.impl.dv.util.HexBin;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.Cipher;
/**
* @author blog.unclezs.com
* @since 2022/5/17 11:42
*/
public class ECCSample {
public static final String ALGORITHM = "EC";
public static final String SING_ALGORITHM = "SHA1withECDSA";
public static final String PROVIDER = "BC";
public static final String ECIES = "ECIES";
static {
Security.addProvider(new BouncyCastleProvider());
}
public static void main(String[] args) throws Exception {
SimpleKeyPair simpleKeyPair = generatePublicKeyAndPrivateKey();
PrivateKey privateKey = parsePrivateKey(simpleKeyPair.getPrivateKey());
PublicKey publicKey = parsePublicKey(simpleKeyPair.getPublicKey());
String plainData = "mydata";
// 签名
String signed = sign(privateKey, plainData);
System.out.println("签名: " + signed);
boolean verifySuccess = verifySign(publicKey, plainData, signed);
System.out.println("验证签名:" + verifySuccess);
// 公钥加密 私钥解密
byte[] encrypted = encrypt(publicKey, plainData);
byte[] decrypted = decrypt(privateKey, encrypted);
System.out.println(new String(decrypted));
}
/**
* 生成公钥和私钥
*
* @throws NoSuchAlgorithmException 没有这样算法异常
*/
public static SimpleKeyPair generatePublicKeyAndPrivateKey()
throws NoSuchAlgorithmException, NoSuchProviderException {
// 获取指定算法的密钥对生成器
KeyPairGenerator generator = KeyPairGenerator.getInstance(ALGORITHM, PROVIDER);
// 初始化密钥对生成器(指定密钥长度, 使用默认的安全随机数源)
generator.initialize(256);
// 随机生成一对密钥(包含公钥和私钥)
KeyPair keyPair = generator.generateKeyPair();
// 获取 公钥 和 私钥
PublicKey pubKey = keyPair.getPublic();
PrivateKey priKey = keyPair.getPrivate();
System.out.println("私钥规范:" + priKey.getFormat());
System.out.println("公钥规范:" + pubKey.getFormat());
// 获取 公钥和私钥 的 编码格式(通过该 编码格式 可以反过来 生成公钥和私钥对象)
byte[] pubEncBytes = pubKey.getEncoded();
byte[] priEncBytes = priKey.getEncoded();
// 把 公钥和私钥 的 编码格式 转换为 Base64文本 方便保存
String pubEncBase64 = new BASE64Encoder().encode(pubEncBytes);
String priEncBase64 = new BASE64Encoder().encode(priEncBytes);
System.out.println("====公钥 开始=====");
System.out.println(pubEncBase64);
System.out.println("====公钥 结束=====");
System.out.println("====私钥 开始=====");
System.out.println(priEncBase64);
System.out.println("====私钥 结束=====");
return new SimpleKeyPair(pubEncBase64, priEncBase64);
}
/**
* 签名
*
* @param key 关键
* @return {@link String}
* @throws Exception 异常
*/
public static String sign(PrivateKey key, String original) throws Exception {
Signature signature = Signature.getInstance(SING_ALGORITHM);
signature.initSign(key);
signature.update(original.getBytes());
byte[] sign = signature.sign();
return HexBin.encode(sign);
}
/**
* 验证签名
*
* @param key 关键
* @param sign 标志
* @return boolean
* @throws Exception 异常
*/
public static boolean verifySign(PublicKey key, String original, String sign) throws Exception {
Signature signature = Signature.getInstance(SING_ALGORITHM);
signature.initVerify(key);
signature.update(original.getBytes());
return signature.verify(HexBin.decode(sign));
}
/**
* 解析公钥
*
* @param publicKeyBase64 公钥base64
* @return {@link PublicKey}
* @throws Exception 异常
*/
public static PublicKey parsePublicKey(String publicKeyBase64) throws Exception {
byte[] keyBytes = new BASE64Decoder().decodeBuffer(publicKeyBase64);
// 创建 已编码的公钥规格
X509EncodedKeySpec encPubKeySpec = new X509EncodedKeySpec(keyBytes);
// 获取指定算法的密钥工厂, 根据 已编码的公钥规格, 生成公钥对象
return KeyFactory.getInstance(ALGORITHM, PROVIDER).generatePublic(encPubKeySpec);
}
/**
* 解析私钥
*
* @param privateKeyBase64 私钥base64
* @return {@link PrivateKey}
* @throws Exception 异常
*/
public static PrivateKey parsePrivateKey(String privateKeyBase64) throws Exception {
byte[] keyBytes = new BASE64Decoder().decodeBuffer(privateKeyBase64);
// 创建 已编码的公钥规格
PKCS8EncodedKeySpec encPubKeySpec = new PKCS8EncodedKeySpec(keyBytes);
// 获取指定算法的密钥工厂, 根据 已编码的公钥规格, 生成公钥对象
return KeyFactory.getInstance(ALGORITHM, PROVIDER).generatePrivate(encPubKeySpec);
}
/**
* 加密
*
* @param key key
* @param plainData 简单数据
* @return {@link byte[]}
* @throws Exception 异常
*/
public static byte[] encrypt(Key key, String plainData) throws Exception {
// 获取指定算法的密码器
Cipher cipher = Cipher.getInstance(ECIES, PROVIDER);
// 初始化密码器
cipher.init(Cipher.ENCRYPT_MODE, key);
// 加密数据, 返回加密后的密文
return cipher.doFinal(plainData.getBytes(StandardCharsets.UTF_8));
}
/**
* 解密
*
* @param key key
* @param cipherData 密码数据
* @return {@link byte[]}
* @throws Exception 异常
*/
private static byte[] decrypt(Key key, byte[] cipherData) throws Exception {
// 获取指定算法的密码器
Cipher cipher = Cipher.getInstance(ECIES, PROVIDER);
// 初始化密码器
cipher.init(Cipher.DECRYPT_MODE, key);
// 解密数据, 返回解密后的明文
return cipher.doFinal(cipherData);
}
public static class SimpleKeyPair {
private String publicKey;
private String privateKey;
public SimpleKeyPair(String publicKey, String privateKey) {
this.publicKey = publicKey;
this.privateKey = privateKey;
}
public String getPublicKey() {
return publicKey;
}
public void setPublicKey(String publicKey) {
this.publicKey = publicKey;
}
public String getPrivateKey() {
return privateKey;
}
public void setPrivateKey(String privateKey) {
this.privateKey = privateKey;
}
}
}
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
输出
私钥规范:PKCS#8
公钥规范:X.509
====公钥 开始=====
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEat8UR/nNE/eNDb84kffQ8oTuK8Ku6Yogm2F6o7CW
Wv589wS0W/8hHBB97z1KykoSiTt4tsmtpKLPmWhdzqJEhA==
====公钥 结束=====
====私钥 开始=====
MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQg5viTq48pVUfB0u9nFoL8V0qmBDCs
gmZKC0wxRo7lSuygCgYIKoZIzj0DAQehRANCAARq3xRH+c0T940NvziR99DyhO4rwq7piiCbYXqj
sJZa/nz3BLRb/yEcEH3vPUrKShKJO3i2ya2kos+ZaF3OokSE
====私钥 结束=====
签名: 304402204828A90CF9232A72876722456EE6241136E95B7395DE549CFC86EAC08D02A38F02200FAB992AE8FBE707D531727F363BD3650419EFE970FCBF54D17ACD4EF5C37E38
验证签名:true
mydata
2
3
4
5
6
7
8
9
10
11
12
13
14
# SM2 实践
package com.unclezs.samples.crypto;
import com.sun.org.apache.xerces.internal.impl.dv.util.HexBin;
import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.jce.spec.ECPrivateKeySpec;
import org.bouncycastle.jce.spec.ECPublicKeySpec;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.encoders.Hex;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.Security;
import java.security.Signature;
/**
* @author blog.unclezs.com
* @since 2022/5/17 19:04
*/
public class SM2Sample {
public static final String ALGORITHM = "EC";
public static final String SING_ALGORITHM = "SM3withSM2";
public static final String PROVIDER = "BC";
public static final String CRYPTO_NAME_SM2 = "sm2p256v1";
public static final String SECURE_RANDOM_ALGORITHM = "SHA1PRNG";
public static final String NOT_COMPRESS_FLG = "04";
static {
if (Security.getProvider(PROVIDER) == null) {
Security.addProvider(new BouncyCastleProvider());
}
}
public static void main(String[] args) throws Exception {
SimpleKeyPair simpleKeyPair = generatePublicKeyAndPrivateKey();
PrivateKey privateKey = parsePrivateKey(simpleKeyPair.getPrivateKey());
PublicKey publicKey = parsePublicKey(simpleKeyPair.getPublicKey());
String plainData = "mydata";
// 签名
String signed = sign(privateKey, plainData);
System.out.println("签名: " + signed);
boolean verifySuccess = verifySign(publicKey, plainData, signed);
System.out.println("验证签名:" + verifySuccess);
// 公钥加密 私钥解密
byte[] encrypted = encrypt(simpleKeyPair.getPublicKey(), plainData);
String encryptedHex = HexBin.encode(encrypted);
System.out.println("密文:" + encryptedHex);
byte[] decrypted = decrypt(simpleKeyPair.getPrivateKey(), encryptedHex);
System.out.println("原文:" + new String(decrypted));
}
/**
* 生成公钥和私钥
*
* @throws java.security.NoSuchAlgorithmException 没有这样算法异常
*/
public static SimpleKeyPair generatePublicKeyAndPrivateKey() throws Exception {
// 获取一条SM2曲线参数
X9ECParameters sm2Parameters = GMNamedCurves.getByName(CRYPTO_NAME_SM2);
// 1.创建密钥生成器
KeyPairGenerator generator = KeyPairGenerator.getInstance(ALGORITHM, PROVIDER);
// 2.初始化生成器,带上随机数
generator.initialize(new ECParameterSpec(sm2Parameters.getCurve(), sm2Parameters.getG(), sm2Parameters.getN()));
// 随机生成一对密钥(包含公钥和私钥)
KeyPair keyPair = generator.generateKeyPair();
// 获取 公钥 和 私钥
PublicKey pubKey = keyPair.getPublic();
PrivateKey priKey = keyPair.getPrivate();
System.out.println("私钥规范:" + priKey.getFormat());
System.out.println("公钥规范:" + pubKey.getFormat());
// 获取 公钥和私钥 的 编码格式(通过该 编码格式 可以反过来 生成公钥和私钥对象)
String pubEncHex = Hex.toHexString(((BCECPublicKey) keyPair.getPublic()).getQ().getEncoded(false));
String priEncHex = ((BCECPrivateKey) keyPair.getPrivate()).getD().toString(16);
// 把 公钥和私钥 的 编码格式 转换为 Base64文本 方便保存
System.out.println("====公钥 开始=====");
System.out.println(pubEncHex);
System.out.println("====公钥 结束=====");
System.out.println("====私钥 开始=====");
System.out.println(priEncHex);
System.out.println("====私钥 结束=====");
return new SimpleKeyPair(pubEncHex, priEncHex);
}
/**
* 签名
*
* @param key 关键
* @return {@link String}
* @throws Exception 异常
*/
public static String sign(PrivateKey key, String original) throws Exception {
Signature signature = Signature.getInstance(SING_ALGORITHM);
signature.initSign(key);
signature.update(original.getBytes());
byte[] sign = signature.sign();
return HexBin.encode(sign);
}
/**
* 验证签名
*
* @param key 关键
* @param sign 标志
* @return boolean
* @throws Exception 异常
*/
public static boolean verifySign(PublicKey key, String original, String sign) throws Exception {
Signature signature = Signature.getInstance(SING_ALGORITHM);
signature.initVerify(key);
signature.update(original.getBytes());
return signature.verify(HexBin.decode(sign));
}
/**
* 解析公钥
*
* @param publicKeyHex q hex
* @return {@link PublicKey}
* @throws Exception 异常
*/
public static PublicKey parsePublicKey(String publicKeyHex) throws Exception {
X9ECParameters sm2Parameters = GMNamedCurves.getByName(CRYPTO_NAME_SM2);
ECParameterSpec ecParameterSpec =
new ECParameterSpec(sm2Parameters.getCurve(), sm2Parameters.getG(), sm2Parameters.getN());
// 创建 已编码的公钥规格
ECPublicKeySpec publicKeySpec =
new ECPublicKeySpec(sm2Parameters.getCurve().decodePoint(Hex.decode(publicKeyHex)), ecParameterSpec);
// 获取指定算法的密钥工厂, 根据 已编码的公钥规格, 生成公钥对象
return new BCECPublicKey(ALGORITHM, publicKeySpec, BouncyCastleProvider.CONFIGURATION);
}
/**
* 解析私钥
*
* @param privateKeyHex 私钥base64
* @return {@link PrivateKey}
* @throws Exception 异常
*/
public static PrivateKey parsePrivateKey(String privateKeyHex) throws Exception {
X9ECParameters sm2Parameters = GMNamedCurves.getByName(CRYPTO_NAME_SM2);
ECParameterSpec ecParameterSpec =
new ECParameterSpec(sm2Parameters.getCurve(), sm2Parameters.getG(), sm2Parameters.getN());
BigInteger d = new BigInteger(privateKeyHex, 16);
ECPrivateKeySpec privateKeySpec = new ECPrivateKeySpec(d, ecParameterSpec);
return new BCECPrivateKey(ALGORITHM, privateKeySpec, BouncyCastleProvider.CONFIGURATION);
}
/**
* 加密
*
* @param key key
* @param plainData 简单数据
* @return {@link byte[]}
* @throws Exception 异常
*/
public static byte[] encrypt(String key, String plainData) throws Exception {
// 获取一条SM2曲线参数
X9ECParameters sm2Parameters = GMNamedCurves.getByName(CRYPTO_NAME_SM2);
// 构造 ECC 算法参数,曲线方程、椭圆曲线 G 点、大整数 N
ECDomainParameters domainParameters =
new ECDomainParameters(sm2Parameters.getCurve(), sm2Parameters.getG(), sm2Parameters.getN());
// 提取公钥点
ECPoint pukPoint = sm2Parameters.getCurve().decodePoint(Hex.decode(key));
// 公钥前面的 02 或者 03 表示是压缩公钥,04 表示未压缩公钥, 04 的时候,可以去掉前面的04
ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(pukPoint, domainParameters);
// C1C3C2 为国密标准
SM2Engine engine = new SM2Engine(SM2Engine.Mode.C1C3C2);
// 设置sm2为加密模式
engine.init(true, new ParametersWithRandom(publicKeyParameters, SecureRandom.getInstance(SECURE_RANDOM_ALGORITHM)));
byte[] in = plainData.getBytes();
return engine.processBlock(in, 0, in.length);
}
/**
* 解密
*
* @param key key
* @param cipherData 密码数据 HEX
* @return {@link byte[]}
* @throws Exception 异常
*/
private static byte[] decrypt(String key, String cipherData) throws Exception {
// 使用BC库加解密时密文以 04 开头,传入的密文前面没有 04 则补上
if (!cipherData.startsWith(NOT_COMPRESS_FLG)) {
cipherData = NOT_COMPRESS_FLG + cipherData;
}
byte[] cipherDataByte = Hex.decode(cipherData);
// 获取一条SM2曲线参数
X9ECParameters sm2Parameters = GMNamedCurves.getByName(CRYPTO_NAME_SM2);
// 构造domain参数
ECDomainParameters domainParameters =
new ECDomainParameters(sm2Parameters.getCurve(), sm2Parameters.getG(), sm2Parameters.getN());
BigInteger privateKeyD = new BigInteger(key, 16);
ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(privateKeyD, domainParameters);
// C1C3C2 为国密标准
SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);
// 设置sm2为解密模式
sm2Engine.init(false, privateKeyParameters);
return sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length);
}
public static class SimpleKeyPair {
private String publicKey;
private String privateKey;
public SimpleKeyPair(String publicKey, String privateKey) {
this.publicKey = publicKey;
this.privateKey = privateKey;
}
public String getPublicKey() {
return publicKey;
}
public void setPublicKey(String publicKey) {
this.publicKey = publicKey;
}
public String getPrivateKey() {
return privateKey;
}
public void setPrivateKey(String privateKey) {
this.privateKey = privateKey;
}
}
}
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
输出
私钥规范:PKCS#8
公钥规范:X.509
====公钥 开始=====
04718081d3ddf9212812ef025040dc09eeb6bd71b99dfe6d246e6d8ddb50eacf18a03d3b3abb5896638e708fd4cc11668497af518cd54e0a6fa93cb176c2a86014
====公钥 结束=====
====私钥 开始=====
fcc1ee85671bb2e2e9a60f5dc5ddb7cdf3cf4d8ea462df7ffc3e33abf1787dfa
====私钥 结束=====
签名: 304502202C5FFA8374A1F6AC5C8881DAF49F40115B4CB9C7ADB42F7EB15338811FC4BDA3022100DCDC90D1A0D5997B7A16C66D5F09333FBDEBA2AE491E99C6FC7648DDCC284127
验证签名:true
密文:046813CCA39081854B6E2A9D63B3C54F91FA38E339A5BE72EB9B66902EC96100009A1D899689BB01B3F83CA9B4A64E5CF1ADAE2C15286BD577DDA8213B2C422F9729E8095F0A0DB8D42C1AC1826D0EE088833F1B0BBA3AC5E4EE4D5CCCDFF72E595134CCF416C7
原文:mydata
2
3
4
5
6
7
8
9
10
11
12