文章目录
一、加密算法
SM2加密是指使用公钥对数据明文进行加密计算,生成相应密文的过程。
加密算法如下:
设需要发送的消息为比特串M,klen为M的比特长度。
为了对明文M进行加密,作为加密者的用户A应实现以下运算步骤:
A1:用随机数发生器产生随机数k∈[1,n-1];
A2:计算椭圆曲线点C1=[k]G=(x1,y1),按本文本第1部分4.2.8和4.2.4给出的细节,将C1的数据类型转换为比特串;
A3:计算椭圆曲线点S=[h]PB,若S是无穷远点,则报错并退出;
A4:计算椭圆曲线点[k]PB=(x2,y2),按本文本第1部分4.2.5和4.2.4给出的细节,将坐标x2、y2的数据类型转换为比特串;
A5:计算t=KDF(x2||y2, klen),若t为全0比特串,则返回A1;
A6:计算C2 = M⊕t;
A7:计算C3 = Hash(x2||M||y2);
A8:输出密文C = C1||C3||C2。
注:C1为0x04||x分量||y分量;C3为SM3的摘要值,长度为32字节;C2为密文其长度和明文相同。
二、解密算法
SM2解密是指使用私钥对密文进行解密计算,还原对应明文的过程。
解密算法如下:
设klen为密文中C2的比特长度。
为了对密文C=C1||C3||C2进行解密,作为解密者的用户B应实现以下运算步骤:
B1:从C中取出比特串C1,按本文本第1部分4.2.3和4.2.9给出的细节,将C1的数据类型转换为椭圆曲线上的点,验证C1是否满足椭圆曲线方程,若不满足则报错并退出;
B2:计算椭圆曲线点S=[h]C1,若S是无穷远点,则报错并退出;
B3:计算[dB]C1=(x2,y2),按本文本第1部分4.2.5和4.2.4给出的细节,将坐标x2、y2的数据类型转换为比特串;
B4:计算t=KDF(x2||y2,klen),若t为全0比特串,则报错并退出;
B5:从C中取出比特串C2,计算M′=C2⊕t;
B6:计算u=Hash(x2||M′||y2),从C中取出比特串C3,若u̸=C3,则报错并退出;
B7:输出明文M′。
三、加解密实例
前面主要阐述了国密SM2加解密算法,密文的组装模式目前有两种:旧模式为C1||C2||C3、新模式为C1||C3||C2。BC密文默认使用的是旧模式,下面通过实例进行验证。在实际使用中,部分设备或平台的密文使用的是旧模式C1||C2||C3,因此需要我们根据国密SM2密文规则重新组装密文数据,以便顺利解密得到原文。
下面将采用BC库进行SM2加解密的演示,BC版本为1.62。
1.采用BC库进行SM2加解密
首先生成SM2的密钥对,然后采用公钥进行数据加密,私钥进行数据解密:
KeyPair keyPair = this.getKeyPair();
// 公钥加密》私钥解密
String original = "test";
log.info("原文:{}", original);
log.info("原文编码:{}", HexUtil.encodeHexStr(original.getBytes()));
// 加密
Cipher cipher = Cipher.getInstance("SM2");
cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic());
byte[] secret = cipher.doFinal(original.getBytes());
log.info("密文字节:{}", secret);
log.info("密文编码:{}", HexUtil.encodeHexStr(secret));
// 解密
cipher = Cipher.getInstance("SM2");
cipher.init(Cipher.DECRYPT_MODE, keyPair.getPrivate());
secret = cipher.doFinal(secret);
log.info("解密:{}", new String(secret));
运行结果:
私钥:308193020100301306072a8648ce3d020106082a811ccf5501822d04793077020101042007243b0e89800f21404e266d14d46e5cf3385fd5c938b1bf97a79de538f8d018a00a06082a811ccf5501822da14403420004842dfe79ffc1461556e053ea7db6a6e1d66a7e617c7ea18a3f0c28659bf4ffd7426d69d8d4e0696b95fe7f38196868b6276a8cd65fbfcf9b6396daef54ec9a18
公钥:3059301306072a8648ce3d020106082a811ccf5501822d03420004842dfe79ffc1461556e053ea7db6a6e1d66a7e617c7ea18a3f0c28659bf4ffd7426d69d8d4e0696b95fe7f38196868b6276a8cd65fbfcf9b6396daef54ec9a18
原文:test
原文编码:74657374
密文字节:[4, 52, -72, 74, -85, 42, -47, -75, -89, 22, 67, 51, -97, -63, 35, 114, 101, 125, -52, -71, -19, -19, -96, 32, 107, 15, 23, 125, -86, 11, 114, 105, 17, 99, -97, -79, 75, 56, -95, 107, 87, -52, -122, -98, -50, 34, -9, -83, -124, -87, 76, -6, -3, 103, 54, -22, -94, -70, -128, -116, 112, -113, -79, -16, -65, -117, 34, 115, 120, -102, -53, -122, 54, 14, -108, 33, -45, -107, 16, -108, -2, 20, 28, 77, 95, -3, -44, 3, -80, 112, -69, -69, 120, 6, 118, -106, 77, -95, 43, 120, -84]
密文编码:0434b84aab2ad1b5a71643339fc12372657dccb9ededa0206b0f177daa0b726911639fb14b38a16b57cc869ece22f7ad84a94cfafd6736eaa2ba808c708fb1f0bf8b2273789acb86360e9421d3951094fe141c4d5ffdd403b070bbbb780676964da12b78ac
解密:test
同样的方式,如果采用私钥加密,公钥解密,则会抛出异常。这就和国密SM2规定的加解密方式一致。
为了验证SM2密文的组装模式,下面通过显示的方式进行解密,即指定待解密的密文组装模式。如下:
KeyPair keyPair = this.getKeyPair();
// 公钥加密》私钥解密
String original = "test";
log.info("原文:{}", original);
log.info("原文编码:{}", HexUtil.encodeHexStr(original.getBytes()));
// 加密
Cipher cipher = Cipher.getInstance("SM2");
cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic());
byte[] secret = cipher.doFinal(original.getBytes());
log.info("密文字节:{}", secret);
log.info("密文编码:{}", HexUtil.encodeHexStr(secret));
// 解密
SM2Engine sm2Engine = new SM2Engine(Mode.C1C2C3);
sm2Engine.init(false, ECUtil.generatePrivateKeyParameter(keyPair.getPrivate()));
secret = sm2Engine.processBlock(secret, 0, secret.length);
log.info("解密:{}", new String(secret));
执行结果:成功解开密文。
可以看出BC库加密后的密文默认使用C1C2C3模式,如果采用C1C3C2模式进行解密,则会出现解密失败。
2.转换SM2密文组装模式
在实际工作中,可能接收到的密文数据组装模式和解密时采用的模式不一样,就需要我们手动进行数据模式转换。下面将C1C2C3模式的密文转换成C1C3C2模式,然后显示采用C1C3C2模式解密数据:
KeyPair keyPair = this.getKeyPair();
// 公钥加密》私钥解密
String original = "test";
log.info("原文:{}", original);
log.info("原文编码:{}", HexUtil.encodeHexStr(original.getBytes()));
// 加密
Cipher cipher = Cipher.getInstance("SM2");
cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPublic());
byte[] secret = cipher.doFinal(original.getBytes());
log.info("密文字节:{}", secret);
log.info("密文编码:{}", HexUtil.encodeHexStr(secret));
// 密文重组 C1C2C3 > C1C3C2
ByteArrayOutputStream cipherTxt = new ByteArrayOutputStream();
cipherTxt.write(secret, 0 ,65);
cipherTxt.write(secret, secret.length -32, 32);
cipherTxt.write(secret, 65, secret.length -32 - 65);
// 解密
SM2Engine sm2Engine = new SM2Engine(Mode.C1C3C2);
sm2Engine.init(false, ECUtil.generatePrivateKeyParameter(keyPair.getPrivate()));
secret = sm2Engine.processBlock(cipherTxt.toByteArray(), 0, cipherTxt.size());
log.info("解密:{}", new String(secret));
密文数据段重组主要是从原来的密文中分别提取出C1、C2、C3段的数据,然后数据段重新排序成C1||C3||C2即可。
运行结果:
私钥:308193020100301306072a8648ce3d020106082a811ccf5501822d047930770201010420c92ea67de3cb2b1e3eda3c4610341323ae5defda712d94a41727e5d0ead7343fa00a06082a811ccf5501822da144034200046e25921c81bbd573bf771a1bde08b384e9e1afa49002ce58bf34b0e3457bf10d566cbc6f76e78d039791d7fca091a75325c332feb97bc455f3a1cf8cc928434a
公钥:3059301306072a8648ce3d020106082a811ccf5501822d034200046e25921c81bbd573bf771a1bde08b384e9e1afa49002ce58bf34b0e3457bf10d566cbc6f76e78d039791d7fca091a75325c332feb97bc455f3a1cf8cc928434a
原文:test
原文编码:74657374
密文字节:[4, 13, -101, 70, -72, -113, 23, 76, -112, 68, 31, 7, -93, -103, 48, -53, -105, 53, -71, 65, -72, 24, 115, -90, 43, 94, -42, 95, -120, 88, -76, -42, 102, 117, 100, 79, 3, -73, -91, -41, -76, 31, 72, -49, -36, -71, -110, 113, 28, 59, 124, -80, 21, 103, -43, -4, 111, 68, 26, -101, 110, -110, -51, -125, 87, 25, -42, 3, -92, -15, 9, 91, 36, 104, -27, 93, 95, 3, -23, 27, -22, -69, 93, -17, 36, 115, -43, -38, -111, 120, 101, -34, -48, 30, -100, -65, -108, 35, 85, -107, -36]
密文编码:040d9b46b88f174c90441f07a39930cb9735b941b81873a62b5ed65f8858b4d66675644f03b7a5d7b41f48cfdcb992711c3b7cb01567d5fc6f441a9b6e92cd835719d603a4f1095b2468e55d5f03e91beabb5def2473d5da917865ded01e9cbf94235595dc
转换密文:040d9b46b88f174c90441f07a39930cb9735b941b81873a62b5ed65f8858b4d66675644f03b7a5d7b41f48cfdcb992711c3b7cb01567d5fc6f441a9b6e92cd8357f1095b2468e55d5f03e91beabb5def2473d5da917865ded01e9cbf94235595dc19d603a4
解密:test
参考资料
全部评论