前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SM? SM1? SM2? SM3? SM4?

SM? SM1? SM2? SM3? SM4?

作者头像
Java技术编程
发布2021-03-04 14:30:55
2.4K0
发布2021-03-04 14:30:55
举报
文章被收录于专栏:Java技术编程

最近看了一个项目的代码,用到了SM2,SM3,SM4,瞬间懵逼,一会用SM2,一会用SM3,一会又用SM4SM???

简单来说,SM2,SM3,SM4 是国家密码局认定的国产密码算法,当然除了这几个算法,还有SM1,SM7,SM9,祖冲之密码算法等。

本文重点是SM算法的Java实现,并不是研究这几种算法的原理以及和其他算法的比较等。

这里简单介绍下这几种SM算法,先有个大概的印象:

SM1

它是一种对称加密算法中的分组加密算法,分组长度、秘钥长度都是128位,和国际上通用的 AES, DES一样,SM1 算法目前还没公开,只能集成在芯片中。

SM2

和国际上通过的RSA一样,是一种非对称加密算法,使用公钥加密,私钥解密,在安全性和运算速度方面要优于RSA算法。

SM3

可以用来生成信息摘要,如MD5,生成的信息摘要长度为256位。

SM4

是一种对称加密算法,可用于替代DES/AES等国际算法, SM4算法与AES算法具有相同的密钥长度和分组长度,都是128位。

当然这些算法的源码可以在商用密码检测中心中下载,我已经下载好了,公众号回复【SM】即可下载。

了解了SM2, SM3, SM4这些算法之后,在我们的系统中就可以应用这些算法来对传输的报文进行加解密;一个简单的流程图如下所示:

SM2

下面将来重点介绍SM2算法

SM2算法是国家密码管理局发布的椭圆曲线公钥密码算法,推荐使用素数域256位椭圆曲线

椭圆曲线方程:

曲线参数为:

如果你数学够屌,文档给你准备好了,公众号回复【SM】即可下载。

SM2 java实现

既然SM2算法那么厉害,接下来我们就使用Java来实现下吧。

首先在项目中引入对应的jar包:

代码语言:javascript
复制
<dependency>
 <groupId>org.bouncycastle</groupId>
 <artifactId>bcprov-jdk15on</artifactId>
 <version>1.60</version>
</dependency>

首先第一步,是要创建 SM2 的椭圆曲线参数,用到的类为 「SM2P256V1Curve」,它是代表国密SM2推荐参数定义的椭圆曲线,源码如下:

可以看到和上面所提供的推荐参数是一样的。

由于「SM2P256V1Curve」没有实现Gx 和 Gy,即基点G的x,y坐标,我们需要自行创建,参数为推荐的参数即可,然后使用x,y 坐标创建基点G,代码如下:

第一步创建 SM2 的椭圆曲线参数的代码如下:

第二步,由于SM2算法基于 ECC,所以需要根据曲线参数来生成 ECC密钥对,代码如下:

第三步,通过 ECC 密钥对获取SM2公钥和私钥,代码如下:

第四步,获取到公钥之后,就可以使用公钥对报文进行加密了,代码如下:

第五步,加密之后,需要使用私钥来进行解密,代码如下:

第六步,现在来测试一下吧。

运行结果如下:

到这里使用Java来实现SM2加解密算法已经结束了,是不是很简单,你学废了吗?

完成代码如下:

生成SM2公私钥:

代码语言:javascript
复制
private static Map<String, ECKeyParameters> getSm2PrivateKeyOrPublicKey() {
 // P256V1Curve 代表国密SM2推荐参数定义的椭圆曲线:
 SM2P256V1Curve sm2P256V1Curve = new SM2P256V1Curve();
 BigInteger sm2N = sm2P256V1Curve.getOrder();
 BigInteger sm2H = sm2P256V1Curve.getCofactor();
 // 由于 gx,gy 没有提供,需要自己创建默认参数
 BigInteger sm2Gx = new BigInteger("32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16);
 BigInteger sm2Gy = new BigInteger("BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0", 16);
 // 根据 gx, gy 坐标创建一个 G点,即为椭圆的基点
 ECPoint sm2Gpoint = sm2P256V1Curve.createPoint(sm2Gx, sm2Gy);
 //设置椭圆曲线参数
 ECDomainParameters ecDomainParameters = new ECDomainParameters(sm2P256V1Curve, sm2Gpoint, sm2N, sm2H);
 // 生成ECC密钥对
 ECKeyGenerationParameters keyGenerationParameters = new ECKeyGenerationParameters(ecDomainParameters, new SecureRandom());
 ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();
 keyPairGenerator.init(keyGenerationParameters);
 // ECC密钥对
 AsymmetricCipherKeyPair asymmetricCipherKeyPair = keyPairGenerator.generateKeyPair();
 // 通过 ECC 密钥对获取SM2公钥和私钥
 // 私钥
 ECPrivateKeyParameters sm2PrivateKey = (ECPrivateKeyParameters) asymmetricCipherKeyPair.getPrivate();
 String x = ByteUtils.toHexString(sm2PrivateKey.getParameters().getG().getAffineXCoord().getEncoded());
 String y = ByteUtils.toHexString(sm2PrivateKey.getParameters().getG().getAffineYCoord().getEncoded());
 System.out.println("生成的SM2私钥为:" + x + "-" + y);
 // 公钥
 ECPublicKeyParameters sm2PublicKey = (ECPublicKeyParameters) asymmetricCipherKeyPair.getPublic();
 String x1 = ByteUtils.toHexString(sm2PublicKey.getQ().getAffineYCoord().getEncoded());
 String y1 = ByteUtils.toHexString(sm2PublicKey.getQ().getAffineXCoord().getEncoded());
 System.out.println("生成的SM2公钥为:" + x1 + "-" + y1);

 Map<String, ECKeyParameters> map = new HashMap<>(2);
 map.put("private", sm2PrivateKey);
 map.put("public", sm2PublicKey);
 return map;
}

加密:

代码语言:javascript
复制
/**
 * 公钥加密
 *
 * @param sm2PublicKey SM2 公钥
 * @param pack         加密的报文
 * @return 加密后的保温
 * @throws Exception e
 */
private static String sm2Encrypt(ECPublicKeyParameters sm2PublicKey, String pack) throws Exception {
 SM2Engine sm2PublicEngine = new SM2Engine();
 ParametersWithRandom withRandom = new ParametersWithRandom(sm2PublicKey, new SecureRandom());
 sm2PublicEngine.init(true, withRandom);
 byte[] encryResult = sm2PublicEngine.processBlock(pack.getBytes(StandardCharsets.UTF_8), 0, pack.getBytes(StandardCharsets.UTF_8).length);
 String encryResultStr = Base64.getEncoder().encodeToString(encryResult);
 System.out.println("加密后的报文为:" + encryResultStr);
 return encryResultStr;
}

解密:

代码语言:javascript
复制
/**
 * SM2 私钥解密
 *
 * @param sm2PrivateKey SM2 私钥
 * @param pack          解密报文
 * @throws Exception e
 */
private static void sm2Decrypt(ECPrivateKeyParameters sm2PrivateKey, String pack) throws Exception {
 SM2Engine sm2PrivateEngine = new SM2Engine();
 sm2PrivateEngine.init(false, sm2PrivateKey);
 byte[] decodePackByte = Base64.getDecoder().decode(pack);
 byte[] decryptResult = sm2PrivateEngine.processBlock(decodePackByte, 0, decodePackByte.length);
 String decryptResultStr = new String(decryptResult, StandardCharsets.UTF_8);
 System.out.println("解密后的报文为:" + decryptResultStr);
}

测试:

代码语言:javascript
复制
static {
 Security.addProvider(new BouncyCastleProvider());
}

public static void main(String[] args) throws Exception {
 Map<String, ECKeyParameters> sm2Key = getSm2PrivateKeyOrPublicKey();
 String pack = "公众号搜索【Java技术编程】, 公众号搜索【Java技术编程】, 公众号搜索【Java技术编程】";
 System.out.println("原始报文为:" + pack);
 // 对报文进行加密
 String encryResult = sm2Encrypt((ECPublicKeyParameters) sm2Key.get("public"), pack);
 // 对报文进行解密
 sm2Decrypt((ECPrivateKeyParameters) sm2Key.get("private"), encryResult);
}

SM3 Java 实现

下一篇,敬请期待。

SM4 Java 实现

下下一篇,敬请期待。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-02-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Java技术大杂烩 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • SM1
  • SM2
  • SM3
  • SM4
  • SM2
    • SM2 java实现
    • SM3 Java 实现
    • SM4 Java 实现
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档