2024-03-21
原文作者:小小工匠 原文地址: https://artisan.blog.csdn.net/article/details/135735891

202403212040534911.png

Pre

Java - 深入理解加密解密和签名算法


概述

国密即国家密码局认定的国产密码算法。主要有SM1,SM2,SM3,SM4。密钥长度和分组长度均为128位。

国密算法是指国家密码管理局认定的一系列国产密码算法,包括SM1-SM9以及ZUC等。其中

  • SM1、SM4、SM5、SM6、SM7、SM8、ZUC等属于对称密码,
  • SM2、SM9等属于公钥密码 (非对称加密)
  • SM3属于单向散列函数。

目前我国主要使用公开的SM2、SM3、SM4作为商用密码算法。

其中SM1、SM7算法不公开,调用该算法时,需要通过加密芯片的接口进行调用

  • SM2是基于椭圆曲线的公钥密码算法,包括用于数字签名的SM2-1、用于密钥交换的SM2-2和用于公钥密码的SM2-3。
  • SM3是能够计算出256比特的散列值的单向散列函数,主要用于数字签名和消息认证码。
  • SM4是属于对称密码的一种分组密码算法,分组长度和密钥长度均为128比特。

国密算法从SM1-SM4分别实现了对称、非对称、摘要等算法功能,目前已普遍应用于日常工作生活中的各个方面,如工作中使用的VPN,金融业务中的资金流转、刷卡支付,以及门禁设施、身份认证等。


S1

SM1 算法是分组密码算法,分组长度为128位,密钥长度都为 128 比特,算法安全保密强度及相关软硬件实现性能与 AES 相当,算法不公开,仅以IP核的形式存在于芯片中。

采用该算法已经研制了系列芯片、智能IC卡、智能密码钥匙、加密卡、加密机等安全产品,广泛应用于电子政务、电子商务及国民经济的各个应用领域(包括国家政务通、警务通等重要领域)。


SM2

可以理解为国产RSA。非对称加密,基于ECC。该算法已公开。由于该算法基于ECC,故其签名速度与秘钥生成速度都快于RSA。

SM2椭圆曲线公钥密码算法是我国自主设计的公钥密码算法,包括SM2-1椭圆曲线数字签名算法,SM2-2椭圆曲线密钥交换协议,SM2-3椭圆曲线公钥加密算法,分别用于实现数字签名密钥协商和数据加密等功能。SM2算法与RSA算法不同的是,SM2算法是基于椭圆曲线上点群离散对数难题,相对于RSA算法,256位的SM2密码强度已经比2048位的RSA密码强度要高,但运算速度快于RSA。

SM3

可以理解为国产MD5。消息摘要。可以用MD5作为对比理解。该算法已公开。校验结果为256位。

SM4

可以理解为国产AES。无线局域网标准的分组数据算法。对称加密,密钥长度和分组长度均为128位。

SM9

一种标识密码(IBE)算法,由国家密码管理局于2016年3月28日发布,相关标准为“GM/T 0044-2016 SM9标识密码算法”。主要用于用户的身份认证。SM9的加密强度等同于3072位密钥的RSA加密算法。


使用经验

一般数据发送端都是用SM4对数据内容加密,使用SM3对内容进行摘要,再使用SM2对摘要进行签名。

一般接收端,先用SM2对摘要进行验签,验签成功后就做到了防抵赖,对发送过来的内容进行SM3摘要,看下生成的摘要和验签后的摘要是否一致,用于防篡改。


另外SM4在加密解密需要相同的密钥,这个我们可以通过编写密钥交换模块实现生成相同的密钥。用于SM4对称加密。

关于非对称还要注意几点:

(1)公钥是通过私钥产生的;

(2)公钥加密,私钥解密是加密的过程

(3)私钥加密,公钥解密是签名的过程;

由于SM1、SM4加解密的分组大小为128bit,故对消息进行加解密时,若消息长度过长,需要进行分组,要消息长度不足,则要进行填充。


国密算法的安全性

国密算法,作为国家层面推广的密码算法标准,其安全性经过了严格的审查和评估。

以下是对SM2、SM3和SM4算法安全性的进一步分析:

SM2算法的安全性

SM2算法是一个基于椭圆曲线的公钥密码算法,其安全性主要依赖于椭圆曲线离散对数问题的难度。与RSA算法相比,SM2算法在相同的安全强度下,所需的密钥长度更短,因此,在加密和签名速度上具有一定的优势。此外,SM2算法在设计时也考虑了多种攻击手段,并采用了相应的防护措施,从而确保了其在实际应用中的安全性。

SM3算法的安全性

SM3算法是一个密码杂凑算法,主要用于数字签名和消息认证等场景。其安全性主要体现在以下几个方面:

  1. 输出长度:SM3算法的输出长度为256比特,相比MD5(128比特)和SHA-1(160比特)算法,其输出长度更长,因此具有更高的安全性。
  2. 碰撞攻击:SM3算法在设计时考虑了碰撞攻击的问题,并采用了相应的防护措施。目前,尚未有公开的针对SM3算法的碰撞攻击方法。
  3. 雪崩效应:SM3算法具有雪崩效应,即输入数据的微小变化会导致输出结果的巨大差异。这一特性使得攻击者难以通过猜测或穷举的方式来破解SM3算法。

SM4算法的安全性

SM4算法是一个分组密码算法,主要用于数据的加密和解密。其安全性主要体现在以下几个方面:

  1. 密钥长度:SM4算法的密钥长度为128比特,与AES算法相同。这一长度的密钥足以抵抗目前已知的所有密码攻击方法。
  2. 分组长度:SM4算法的分组长度也为128比特,这意味着每次加密或解密的数据块大小为128比特。这一分组长度可以确保数据的机密性和完整性。
  3. 加密轮数:SM4算法采用了多轮加密的方式,每轮加密都使用了不同的密钥和加密函数。这种加密方式可以使得攻击者难以通过分析加密过程来破解算法。
  4. 安全性评估:SM4算法已经经过了多轮的安全性评估和审查,其安全性得到了广泛的认可。目前,尚未有公开的针对SM4算法的有效攻击方法。

综上所述,国密算法中的SM2、SM3和SM4算法都具有较高的安全性,可以满足不同场景下的密码应用需求。在实际应用中,可以根据具体的需求和场景选择合适的算法进行使用。

202403212040537642.png

在Java中使用

Hutool针对Bouncy Castle做了简化包装,用于实现国密算法中的SM2、SM3、SM4。

国密算法工具封装包括:

  • 非对称加密和签名:SM2
  • 摘要签名算法:SM3
  • 对称加密:SM4

国密算法需要引入Bouncy Castle库的依赖。

使用

引入Bouncy Castle依赖

    <dependency>
      <groupId>org.bouncycastle</groupId>
      <artifactId>bcprov-jdk15to18</artifactId>
      <version>1.69</version>
    </dependency>

说明 bcprov-jdk15to18的版本请前往Maven中央库搜索,查找对应JDK的最新版本。

非对称加密SM2

使用随机生成的密钥对加密或解密

    String text = "我是一段测试aaaa";
    
    SM2 sm2 = SmUtil.sm2();
    // 公钥加密,私钥解密
    String encryptStr = sm2.encryptBcd(text, KeyType.PublicKey);
    String decryptStr = StrUtil.utf8Str(sm2.decryptFromBcd(encryptStr, KeyType.PrivateKey));

使用自定义密钥对加密或解密

    String text = "我是一段测试aaaa";
    
    KeyPair pair = SecureUtil.generateKeyPair("SM2");
    byte[] privateKey = pair.getPrivate().getEncoded();
    byte[] publicKey = pair.getPublic().getEncoded();
    
    SM2 sm2 = SmUtil.sm2(privateKey, publicKey);
    // 公钥加密,私钥解密
    String encryptStr = sm2.encryptBcd(text, KeyType.PublicKey);
    String decryptStr = StrUtil.utf8Str(sm2.decryptFromBcd(encryptStr, KeyType.PrivateKey));

SM2签名和验签

    String content = "我是Hanley.";
    final SM2 sm2 = SmUtil.sm2();
    String sign = sm2.signHex(HexUtil.encodeHexStr(content));
    
    // true
    boolean verify = sm2.verifyHex(HexUtil.encodeHexStr(content), sign);

自定义密钥对

当然,也可以自定义密钥对

    String content = "我是Hanley.";
    KeyPair pair = SecureUtil.generateKeyPair("SM2");
    final SM2 sm2 = new SM2(pair.getPrivate(), pair.getPublic());
    
    byte[] sign = sm2.sign(content.getBytes());
    
    // true
    boolean verify = sm2.verify(content.getBytes(), sign);

使用SM2曲线点构建SM2

使用曲线点构建中的点生成和验证见:https://i.goto327.top/CryptTools/SM2.aspx?tdsourcetag=s_pctim_aiomsg

    String privateKeyHex = "FAB8BBE670FAE338C9E9382B9FB6485225C11A3ECB84C938F10F20A93B6215F0";
    String x = "9EF573019D9A03B16B0BE44FC8A5B4E8E098F56034C97B312282DD0B4810AFC3";
    String y = "CC759673ED0FC9B9DC7E6FA38F0E2B121E02654BF37EA6B63FAF2A0D6013EADF";
    
    // 数据和ID此处使用16进制表示
    String data = "434477813974bf58f94bcf760833c2b40f77a5fc360485b0b9ed1bd9682edb45";
    String id = "31323334353637383132333435363738";
    
    final SM2 sm2 = new SM2(privateKeyHex, x, y);
    // 生成的签名是64位
    sm2.usePlainEncoding();
    
    final String sign = sm2.signHex(data, id);
    // true
    boolean verify = sm2.verifyHex(data, sign)

使用私钥D值签名

    //需要签名的明文,得到明文对应的字节数组
    byte[] dataBytes = "我是一段测试aaaa".getBytes();
    //指定的私钥
    String privateKeyHex = "1ebf8b341c695ee456fd1a41b82645724bc25d79935437d30e7e4b0a554baa5e";
    
    // 此构造从5.5.9开始可使用
    final SM2 sm2 = new SM2(privateKeyHex, null, null);
    sm2.usePlainEncoding();
    byte[] sign = sm2.sign(dataBytes, null);

使用公钥Q值验证签名

    //指定的公钥
    String publicKeyHex ="04db9629dd33ba568e9507add5df6587a0998361a03d3321948b448c653c2c1b7056434884ab6f3d1c529501f166a336e86f045cea10dffe58aa82ea13d725363";
    //需要加密的明文,得到明文对应的字节数组
    byte[] dataBytes = "我是一段测试aaaa".getBytes();
    //签名值
    String signHex ="2881346e038d2ed706ccdd025f2b1dafa7377d5cf090134b98756fafe084dddbcdba0ab00b5348ed48025195af3f1dda29e819bb66aa9d4d088050ff148482a";
    
    final SM2 sm2 = new SM2(null, ECKeyUtil.toSm2PublicParams(publicKeyHex));
    sm2.usePlainEncoding();
    
    // true
    boolean verify = sm2.verify(dataBytes, HexUtil.decodeHex(signHex));

其他格式的密钥

在SM2算法中,密钥的格式分以下几种:

私钥:

  • D值 一般为硬件直接生成的值
  • PKCS#8 JDK默认生成的私钥格式
  • PKCS#1 一般为OpenSSL生成的的EC密钥格式

公钥:

  • Q值 一般为硬件直接生成的值
  • X.509 JDK默认生成的公钥格式
  • PKCS#1 一般为OpenSSL生成的的EC密钥格式

在新版本的Hutool中,SM2的构造方法对这几类的密钥都做了兼容,即用户无需关注密钥类型:

摘要加密算法SM3

    //结果为:136ce3c86e4ed909b76082055a61586af20b4dab674732ebd4b599eef080c9be
    String digestHex = SmUtil.sm3("aaaaa");

对称加密SM4

    String content = "test中文";
    SymmetricCrypto sm4 = SmUtil.sm4();
    
    String encryptHex = sm4.encryptHex(content);
    String decryptStr = sm4.decryptStr(encryptHex, CharsetUtil.CHARSET_UTF_8);

202403212040544953.png

阅读全文