美文网首页
SM系列国密算法使用

SM系列国密算法使用

作者: 肥兔子爱豆畜子 | 来源:发表于2023-05-19 15:31 被阅读0次

SM系列国密算法可以使用Bouncy Castle来编程实现,这里演示如果在Java中去使用,为了方便使用了Hutool,这个库对BC做了简化包装,用于实现国密算法中的SM2,SM3,SM4算法:

  • SM2:非对称加密算法,用来做非对称加解密,以及数字签名。类似rsa
  • SM3:hash摘要算法,用来做数字指纹,类似md5
  • SM4:对称加密算法,用来做加解密,类似aes

程序依赖:

  <dependencies>
    <dependency>
      <groupId>org.bouncycastle</groupId>
      <artifactId>bcprov-jdk15to18</artifactId>
      <version>1.69</version>
    </dependency>
    <dependency>
      <groupId>cn.hutool</groupId>
      <artifactId>hutool-all</artifactId>
      <version>5.8.16</version>
    </dependency>

    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.24</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.7.28</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-simple</artifactId>
      <version>1.7.28</version>
    </dependency>

  </dependencies>

下面是代码:

import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.SM2;
import cn.hutool.crypto.symmetric.SM4;
import lombok.extern.slf4j.Slf4j;

import java.security.KeyPair;

/**
 * 测试sm国密算法,基于Hutool工具
 *
 */
@Slf4j
public class App 
{
    public static void main( String[] args ) throws Exception
    {
        //sm3摘要算法
        log.info("=====> sm3摘要算法演示");
        String hash = SmUtil.sm3("feituzi-ai-douchuzi");
        log.info("sm3 hash: {}", hash);

        //sm4对称加密算法
        log.info("=====> sm4对称加密算法演示");
        String sm4Key = "lovelovelovelove"; //对称密钥, 需要128bit长度
        SM4 sm4 = SmUtil.sm4(sm4Key.getBytes());
        String content = "肥兔子和豆畜子永远在一起";
        String encryptHex = sm4.encryptHex(content, CharsetUtil.charset("utf-8"));
        log.info("sm4算法对称加密明文,得到16进制密文:{}", encryptHex);
        String decryptStr = sm4.decryptStr(encryptHex, CharsetUtil.charset("utf-8"));
        log.info("sm4解密16进制密文,得到明文:{}", decryptStr);

        //sm2非对称加密算法, 加解密,签名验签
        log.info("=====> sm2非对称加密算法演示");
        log.info("生成sm2密钥对:");
        KeyPair keyPair = SecureUtil.generateKeyPair("SM2");
        byte[] sk = keyPair.getPrivate().getEncoded();
        byte[] pk = keyPair.getPublic().getEncoded();
        String skHex = HexUtil.encodeHexStr(sk);
        String pkHex = HexUtil.encodeHexStr(pk);
        log.info("sm2算法, 公钥:{}", pkHex);
        log.info("sm2算法, 私钥:{}", skHex);
        log.info("公钥加密,私钥解密:");
        String text = "豆畜子爱玩魔兽世界";
        SM2 pkSm2 = SmUtil.sm2(null, pk);
        byte[] cryptText = pkSm2.encrypt(text.getBytes("utf-8"), KeyType.PublicKey);
        log.info("sm2加密后16进制密文:{}" , HexUtil.encodeHexStr(cryptText));
        SM2 skSm2 = SmUtil.sm2(sk, null);
        byte[] textb = skSm2.decrypt(cryptText, KeyType.PrivateKey);
        String textStr = new String(textb, "utf-8");
        log.info("sm2私钥解密后明文:{}", textStr);
        log.info("私钥签名,公钥验签:");
        String myWord = "肥兔子说:豆畜子爱玩魔兽世界。我对这句话负责。";
        byte[] sign = skSm2.sign(myWord.getBytes("utf-8"));
        log.info("sm2私钥签名:{}", HexUtil.encodeHexStr(sign));
        boolean result = pkSm2.verify(myWord.getBytes("utf-8"), sign);
        log.info("sm2签名验签结果,{}", result);
    }
}

输出:

[main] INFO org.example.App - =====> sm3摘要算法演示
[main] INFO org.example.App - sm3 hash: 11bfeb02b0e6ffff97f86f2af2a00d929db854ba3b65f5e9bd6be554d5e63154
[main] INFO org.example.App - =====> sm4对称加密算法演示
[main] INFO org.example.App - sm4算法对称加密明文,得到16进制密文:ae6b4d7b3f7e9cfa5299b168505628ab64751bbeccf6725a111c56ee5ab8ebf7539e3558527a64058d8ed4f5415b95d7
[main] INFO org.example.App - sm4解密16进制密文,得到明文:肥兔子和豆畜子永远在一起
[main] INFO org.example.App - =====> sm2非对称加密算法演示
[main] INFO org.example.App - 生成sm2密钥对:
[main] INFO org.example.App - sm2算法, 公钥:3059301306072a8648ce3d020106082a811ccf5501822d03420004054e0fc0bb71650f3bd26aef2ce2910a0658c916d2b633f3432b1fc672b7b9367f33ac5910553029715e169c4be5f3131a629e8991b2f43ea785e57f4d0c1f18
[main] INFO org.example.App - sm2算法, 私钥:308193020100301306072a8648ce3d020106082a811ccf5501822d047930770201010420ffece57e69f6fadaced8abea9ae2734f973b1dcf2212e7b03b466b293579a48da00a06082a811ccf5501822da14403420004054e0fc0bb71650f3bd26aef2ce2910a0658c916d2b633f3432b1fc672b7b9367f33ac5910553029715e169c4be5f3131a629e8991b2f43ea785e57f4d0c1f18
[main] INFO org.example.App - 公钥加密,私钥解密:
[main] INFO org.example.App - sm2加密后16进制密文:0444a91a37a7f6cf59c48c807345543634aa4c52bb08979bb58c4058ad646e669c614600075552fd1af254012facb3597e4f388d29f34ea484309e5cab5409d188b1bc66015d9fe63fd422a4f64836a774a041dfb174a4abd07e1778f1466fbbc5573f61df6a3ae6d4f37a394c73b00cbd50c127534494ea29855145
[main] INFO org.example.App - sm2私钥解密后明文:豆畜子爱玩魔兽世界
[main] INFO org.example.App - 私钥签名,公钥验签:
[main] INFO org.example.App - sm2私钥签名:30460221008fad626a6febebeeba3bbca9ddf7e00233b99543e17f8b4ebae91eb5609174a4022100f76207ae3e3e5ac6b9dfc954b7e8ffccf322160a6d4ca8adc61b3fd92bf8836a
[main] INFO org.example.App - sm2签名验签结果,true

上面有个问题,出来的密钥太长,跟SM2私钥256位、公钥512位的常识不符,参考2说明了原因,按照其提供的方法改造后:

@Slf4j
public class Main {
    public static void main(String[] args) throws Exception{
        SM2 sm2 = SmUtil.sm2();
        log.info("=====> sm2非对称加密算法演示");
        log.info("生成sm2密钥对:");
        byte[] sk = BCUtil.encodeECPrivateKey(sm2.getPrivateKey());
        byte[] pk = ((BCECPublicKey)sm2.getPublicKey()).getQ().getEncoded(false);

        //KeyPair keyPair = SecureUtil.generateKeyPair("SM2");

        //PrivateKey sk = keyPair.getPrivate();
        //PublicKey pk = keyPair.getPublic();
        //log.info("sm2算法, 私钥:{}", sk.getFormat());
        //log.info("sm2算法, 私钥:{}", sk.getAlgorithm());
        String skHex = HexUtil.encodeHexStr(sk);
        String pkHex = HexUtil.encodeHexStr(pk);
        log.info("sm2算法, 公钥:{}", pkHex);
        log.info("sm2算法, 私钥:{}", skHex);
        log.info("公钥加密,私钥解密:");
        String text = "豆畜子爱玩魔兽世界";
        SM2 pkSm2 = SmUtil.sm2(null, pk);
        byte[] cryptText = pkSm2.encrypt(text.getBytes("utf-8"), KeyType.PublicKey);
        log.info("sm2加密后16进制密文:{}" , HexUtil.encodeHexStr(cryptText));
        SM2 skSm2 = SmUtil.sm2(sk, null);
        byte[] textb = skSm2.decrypt(cryptText, KeyType.PrivateKey);
        String textStr = new String(textb, "utf-8");
        log.info("sm2私钥解密后明文:{}", textStr);

        log.info("私钥签名,公钥验签:");
        String myWord = "肥兔子说:豆畜子爱玩魔兽世界。我对这句话负责。";
        byte[] sign = skSm2.sign(myWord.getBytes("utf-8"));
        log.info("sm2签名信息:{}", myWord);
        log.info("sm2私钥签名:{}", HexUtil.encodeHexStr(sign));
        boolean result = pkSm2.verify(myWord.getBytes("utf-8"), sign);
        log.info("sm2签名验签结果,{}", result);
    }

}

输出

[main] INFO org.example.Main - =====> sm2非对称加密算法演示
[main] INFO org.example.Main - 生成sm2密钥对:
[main] INFO org.example.Main - sm2算法, 公钥:04e67bbd97c3e00057c5ce7d6937530801d95aca74a3e8c12469b495262d7aede1772ad06385777f4796a9be3e37fffab71f3966190f39fdb7719d2a7f47a1da23
[main] INFO org.example.Main - sm2算法, 私钥:00de20de8aad3b38ce1b7f2aeb3d0b35a6292433ad5a63d1355799bc53c723e74e
[main] INFO org.example.Main - 公钥加密,私钥解密:
[main] INFO org.example.Main - sm2加密后16进制密文:04af61f0ee01fc490a00870f040d6debd1a9149c9de1a6e43bd073f502301b32cc8aaeaf8640d844c6f0d69abf0fe29fbdcd31c0ed970f55471688c26b7a27ed5004250a1ccbe0d70a7ea966c13fc29dd9e23496cf5abf99fa36abb7b871e785a486f2e1d9ac4a2fd9e35df0aaada1136483c758d1f676f37ac5d886
[main] INFO org.example.Main - sm2私钥解密后明文:豆畜子爱玩魔兽世界
[main] INFO org.example.Main - 私钥签名,公钥验签:
[main] INFO org.example.Main - sm2签名信息:肥兔子说:豆畜子爱玩魔兽世界。我对这句话负责。
[main] INFO org.example.Main - sm2私钥签名:3046022100ca27e1ec38aa3771fda6128b0bad185362307a377aa0815d805c63966d083525022100c9e59a6936fd56ddabd5d950eb26b69f8e3942dc0bf65e0ac343c265d26f7264
[main] INFO org.example.Main - sm2签名验签结果,true

Process finished with exit code 0

这样私钥去掉开头的00后, de20de8aad3b38ce1b7f2aeb3d0b35a6292433ad5a63d1355799bc53c723e74e是64长度的16进制,是256位;而公钥04e67bbd97c3e00057c5ce7d6937530801d95aca74a3e8c12469b495262d7aede1772ad06385777f4796a9be3e37fffab71f3966190f39fdb7719d2a7f47a1da23未压缩状态,前两个'04'是压缩标识,剩下128长度的16进制,刚好是512位bit。

参考

1、https://hutool.cn/docs/#/crypto/%E5%9B%BD%E5%AF%86%E7%AE%97%E6%B3%95%E5%B7%A5%E5%85%B7-SmUtil

2、hutool国密sm2算法使用, 正确的秘钥生成签名及验签,签名为64字节_努力的犀牛的博客-CSDN博客

相关文章

网友评论

      本文标题:SM系列国密算法使用

      本文链接:https://www.haomeiwen.com/subject/vecnsdtx.html