基于JPBC的SM9算法的java实现与测试
⽂章⽬录
SM9算法是国家密码局发布的⼀种IBC算法,包括《GMT 0044-2016 SM9标识密码算法:1~5》⼀系列标准。
PBC(Pairing Based Cryptography)是⼀个双线性对密码学库,包括了很多种语⾔的实现。JPBC是其纯Java语⾔版本的实现。
JPBC中实现了多种类型的双线性映射,⽽实现SM9需要使⽤的"type f"类型。其中最关键两点是曲线参数的确定和R-ate双线性对的计算。另外,SM9算法中还⽤到了SM3和SM4算法,这⾥直接使⽤bouncycastle库。
源码下载
下⾯,简单描述⼀下。
确定参数
type F类型的曲线参数包括:q,r,b,beta,alpha1,alph1,其中的q,r,b和SM9标准定义的⼀致,后3个则略有区别。
JPBC中双线性对参数可以⽤PropertiesParameters保存。经过测试,确定了符合SM9定义标准的type F曲线参数,并在类
SM9CurveParameters中定义如下:
public class SM9CurveParameters {
public static final int nBits = 256; //The number of bits for the order N of the subgroup G1/G2
public static final BigInteger b = BigInteger.valueOf(5);
public static final BigInteger t = new BigInteger("600000000058F98A", 16);
public static final BigInteger q = new BigInteger("B640000002A3A6F1D603AB4FF58EC74521F2934B1A7AEEDBE56F9B27E351457D", 16);
public static final BigInteger N = new BigInteger("B640000002A3A6F1D603AB4FF58EC74449F2934B18EA8BEEE56EE19CD69ECF25", 16);
public static final BigInteger beta = new BigInteger("B640000002A3A6F1D603AB4FF58EC74521F2934B1A7AEEDBE56F9B27E351457B", 16);
public static final BigInteger alpha0 = BigInteger.ZERO;
public static final BigInteger alpha1 = new BigInteger("B640000002A3A6F1D603AB4FF58EC74521F2934B1A7AEEDBE56F9B27E351457C", 16);
public static byte[] P1_bytes = Hex.decode(
"93DE051D62BF718FF5ED0704487D01D6E1E4086909DC3280E8C4E4817C66DDDD"
+ "21FE8DDA4F21E607631065125C395BBC1C1C00CBFA6024350C464CD70A3EA616");
public static byte[] P2_bytes = Hex.decode(
"3722755292130B08D2AAB97FD34EC120EE265948D19C17ABF9B7213BAF82D65B"
+ "85AEF3D078640C98597B6027B441A01FF1DD2C190F5E93C454806C11D8806141"
+ "A7CF28D519BE3DA65F3170153D278FF247EFBA98A71A08116215BBA5C999A7C7"
+ "17509B092E845C1266BA0D262CBEE6ED0736A96FA347C8BD856DC76B84EBEB96");
public static PropertiesParameters createSM9PropertiesParameters() {
PropertiesParameters params = new PropertiesParameters();
params.put("type", "f");
params.put("q", q.toString());
params.put("r", N.toString());
params.put("b", b.toString());
params.put("beta", String());
params.put("alpha0", String());
params.put("alpha1", String());
params.put("t", t.toString());
return params;
}
}
R-ate双线性对
确定了曲线参数,另⼀个重点就是R-ate对的计算了。
⾸先,通过继承TypeFPairing定义了SM9Pairing类,⽤来计算R-ate值;通过继承AbstractPairingMap定义了SM9RatePairingMap 类,是具体的R-ate算法实现。
定义曲线
有了参数和R-ate的实现,就可以定义实现SM9算法所需要的全部因素了。具体定义在SM9Curve类中:
public SM9Curve(SecureRandom random) {
this.random = random;
PairingParameters parameters = ateSM9PropertiesParameters();
this.sm9Pairing = new SM9Pairing(random, parameters);
this.N = N();
this.G1 = (CurveField) G1();
java的tostring方法this.G2 = (CurveField) G2();
this.GT = (GTFiniteField) GT();
//set P1
P1 = G1.newElement();
P1.setFromBytes(SM9CurveParameters.P1_bytes);
//set P2
P2 = G2.newElement();
P2.setFromBytes(SM9CurveParameters.P2_bytes);
}
在这个类中,定义了SM9中的G1、G2和GT,阶N,G1的⽣成元P1,G2的⽣成元P2和计算R-ate对的对象sm9Pairing。
需要注意的是P1和P2这两个⽣成元的设置,需要确保设置后它的infFlag属性是0,因此此处采⽤setFromBytes⽅法进⾏设置。
辅助⽅法
在SM9中,需要⼏个辅助函数,把它们定义在SM9Utils类中,主要包括H1、H2、Hash(即Hv)、MAC、KDF函数。
另外,为了对SM9标准中的测试进⾏验证,还定义3个⽅法⽤来把G1、G2和GT上的点转为和SM9标准中⼀致的数据。因为JPBC中椭圆曲线的元素坐标数据和SM9标准测试中显⽰的顺序是不⼀致的,所以需要转换。但是在元素的实际重构转换中,还是使⽤JPBC⾃带的toBytes⽅法。
其他还包括了⼏个简单的辅助⽅法也放在这个类中。
密钥结构与算法结果值结构
在开始实现SM9算法之前,考虑SM9算法中涉及到的数据结构,包括密钥和算法返回结果值,因为它们可能包含了多个值,所以把它们定义为单独的⼀个类。
同时,提供toByteArray⽅法⽤来把结构对象转化为byte数组,以⽅便传播;还提供⼀个静态⼯⼚⽅法fromByteArray以便能将从toByteArray中得到的byte数组重新正确地转为对应的结构。
另外,还在需要的结构中重写了toString⽅法,主要⽤来打印⽇志,⽅便查看测试数据。
本代码中包括的结构如下表所⽰:
结构类名说明备注
MasterKeyPair主密钥对包括主私钥和主公钥
MasterPrivateKey主私钥[1,N-1]范围内的⼀个随机数
MasterPublicKey主公钥签名主公钥是G2上的⼀个元素;加密主公钥是G1上的⼀个元素;
PrivateKeyType⽤户私钥类型SM9标准中⽤hid数字区分,这⾥定义为⼀个enum结构
PrivateKey⽤户私钥签名私钥是G1上的⼀个元素;加密私钥是G2上的⼀个元素;
G1KeyPair G1上的密钥对包括私钥和公钥,⽤在密钥交换的准备阶段
G1PrivateKey G1上的私钥[1,N-1]范围内的⼀个随机数
结构类名说明备注
G1PublicKey G1上的公钥是⼀个G1上的元素,=P1*私钥
ResultSignature签名值结构包括⼀个⼤整数h和G1上的⼀个元素s
ResultEncapsulate密钥封装值结构包括⼀个byte数组密钥 K 和 ResultEncapsulateCipherText ResultEncapsulateCipherText密钥封装中的密钥密⽂是G1上的⼀个元素
ResultCipherText加密结果值结构包括G1上的⼀个元素C1、原始数据的密⽂C2、⼀个SM3哈希值C3
ResultKeyExchange密钥交换值结构包括⼀个byte数组密钥 SK 和两个SM3哈希值SA2、SB1
有了SM9Curve这个类,就可以开始实现SM9算法的各个部分了。
SM9算法使⽤时主要包括密钥⽣成和算法两个部分,把它们分别定义在KGC类和SM9类中,这两个类都需要SM9Curve参数。
密钥⽣成中⼼KGC
KGC是⽤来⽣成SM9密钥的,包括主密钥对和⽤户私钥。
主密钥对⼜分为签名主密钥对和加密主密钥对。主密钥对包括⼀个公钥和⼀个私钥,私钥就是⼀个[1,N-1]范围内的随机数,签名公钥是P2的私钥倍点,加密公钥是P2的私钥倍点。
⽤户私钥包括签名私钥和加密私钥。⽤户私钥使⽤SM9标准中的hid进⾏标识:
1:签名私钥,⽤于签名
2:加密私钥,解封和解密
3:也是加密私钥,但⽤在密钥交换中
另外,在⽤户私钥⽣成中,有⼀个辅助函数T2也定义在此类中;⽽T1因为只⽤在了T2的计算中,⽽且⽐较简单,所以直接包含在T2中。SM9算法实现
SM9的算法实现放在SM9类中,主要包括:
签名算法和验签算法
密钥封装算法和密钥解封算法
加密算法和解密算法
密钥交换算法
其中密钥交换算法分两步,第⼀步是⽣成G1上的⼀对公私钥,其中公钥需要传递给对⽅;第⼆步才是开始计算共享密钥。
这⾥给出签名算法:
/**
* SM9 sign.
*
* @param masterPublicKey signed master public key
* @param privateKey signed private key
* @param data source data
* @return signature value, including h and s.
*/
public ResultSignature sign(MasterPublicKey masterPublicKey, PrivateKey privateKey, byte[] data)
{
BigInteger l, h;
//Step1 : g = e(P1, Ppub-s)
Element g = mCurve.pairing(mCurve.P1, masterPublicKey.Q);
do {
//Step2: generate r
BigInteger r = Random(mCurve.random, mCurve.N);
//Step3 : calculate w=g^r
Element w = g.duplicate().pow(r);
//Step4 : calculate h=H2(M||w,N)
h = SM9Utils.H2(data, w, mCurve.N);
//Step5 : l=(r-h)mod N
l = r.subtract(h).mod(mCurve.N);
} while(l.equals(BigInteger.ZERO));
//Step6 : S=[l]dSA=(xS,yS)
CurveElement s = privateKey.d.duplicate().mul(l);
//Step7 : signature=(h,s)
return new ResultSignature(h, s);
}
可以看到,基于JPBC来实现SM9算法⽐较简单,基本上是⼀个步骤⼀条语句就可以搞定了,关键还是算法参数的确定和R-ate对的实现。测试《GMT 0044-2016 SM9标识密码算法:第5部分》
为了验证SM9标准中的测试数据,从KGC和SM9继承出了KGCWithStandardTestKey和SM9WithStandardTestKey类。其中的算法实现当然是不变的,只是在其中添加了输出⽇志。
⽇志⼤致和标准中显⽰的⼀致,但有些地⽅还是不⼀致的,有⼀些不重要的就没有显⽰了,⽐如两个数据连接起来后的结果就没有显⽰。要得到和标准测试中⼀致的结果,只要控制算法中的随机数就⾏了,因此在测试各个算法的之前,先设置好随机数,再进⾏就可以了。
对每⼀组算法进⾏单独测试,密钥⽣成包含在其中。以签名验签算法为例,主要有3种:
test_sm9_sign:通⽤使⽤⽅法,实际应⽤中这样⽤就可以了
test_sm9_sign_standard:验证《GMT 0044-2016 SM9标识密码算法:第5部分》,输出⽇志
test_sm9_sign_re:在test_sm9_sign_standard的基础上,对密钥和结果值结构进⾏转换,验证它们是否可以进⾏重构
最后,附上验证测试结果:
----------------------------------------------------------------------
SM9 curve parameters:
b:
05
t:
60000000 0058F98A
q:
B6400000 02A3A6F1 D603AB4F F58EC745 21F2934B 1A7AEEDB E56F9B27 E351457D
B6400000 02A3A6F1 D603AB4F F58EC745 21F2934B 1A7AEEDB E56F9B27 E351457D
N:
B6400000 02A3A6F1 D603AB4F F58EC744 49F2934B 18EA8BEE E56EE19C D69ECF25
beta:
B6400000 02A3A6F1 D603AB4F F58EC745 21F2934B 1A7AEEDB E56F9B27 E351457B
alpha0:
00
alpha1:
B6400000 02A3A6F1 D603AB4F F58EC745 21F2934B 1A7AEEDB E56F9B27 E351457C
P1:
93DE051D 62BF718F F5ED0704 487D01D6 E1E40869 09DC3280 E8C4E481 7C66DDDD 21FE8DDA 4F21E607 63106512 5C395BBC 1C1C00CB FA602435 0C464CD7 0A3EA616
P2:
85AEF3D0 78640C98 597B6027 B441A01F F1DD2C19 0F5E93C4 54806C11 D8806141 37227552 92130B08 D2AAB97F D34EC120 EE265948 D19C17AB F9B7213B AF82D65B 17509B09 2E845C12 66BA0D26 2CBEE6ED 0736A96F A347C8BD 856DC76B 84EBEB96 A7CF28D5 19BE3DA6 5F317015 3D278FF2 47EFBA98 A71A0811 6215BBA5 C999A7C7 ----------------------------------------------------------------------
----------------------------------------------------------------------
SM9签名测试
签名主密钥和⽤户签名私钥产⽣过程中的相关值:
签名主私钥 ks:
sm9 master private key:
000130E7 8459D785 45CB54C5 87E02CF4 80CE0B66 340F319F 348A1D5B 1F2DC5F4
签名主公钥 Ppub-s:
sm9 master public key:
9F64080B 3084F733 E48AFF4B 41B56501 1CE0711C 5E392CFB 0AB1B679 1B94C408 29DBA116 152D1F78 6CE843ED 24A3B573 414D2177 386A92DD 8F14D656 96EA5E32 69850938 ABEA0112 B57329F4 47E3A0CB AD3E2FDB 1A77F335 E89E1408 D0EF1C25 41E00A53 DDA532DA 1A7CE027 B7A46F74 1006E85F 5CDFF073 0E75C05F B4E3216D
实体A的标识IDA:
Alice
IDA的16进制表⽰
416C6963 65
H1:
2ACC468C 3926B0BD B2767E99 FF26E084 DE9CED8D BC7D5FBF 418027B6 67862FAB
t1:
2ACD7773 BD808842 F841D35F 87070D79 5F6AF8F3 F08C915E 760A4511 86B3F59F
t2:
291FE3CA C8F58AD2 DC462C8D 4D578A94 DAFD5624 DDC28E32 8D293668 8A86CF1A
签名私钥 ds_A:
SM9 private key:
A5702F05 CF131530 5E2D6EB6 4B0DEB92 3DB1A0BC F0CAFF90 523AC875 4AA69820 78559A84 4411F982 5C109F5E E3F52D72 0DD01785 392A727B B1556952 B2B013D3
签名步骤中的相关值:
待签名消息 M:
Chinese IBS standard
M的16进制表⽰
4368696E 65736520 49425320 7374616E 64617264
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论