Pre
概述
国密即国家密码局认定的国产密码算法。主要有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算法是一个密码杂凑算法,主要用于数字签名和消息认证等场景。其安全性主要体现在以下几个方面:
- 输出长度:SM3算法的输出长度为256比特,相比MD5(128比特)和SHA-1(160比特)算法,其输出长度更长,因此具有更高的安全性。
- 碰撞攻击:SM3算法在设计时考虑了碰撞攻击的问题,并采用了相应的防护措施。目前,尚未有公开的针对SM3算法的碰撞攻击方法。
- 雪崩效应:SM3算法具有雪崩效应,即输入数据的微小变化会导致输出结果的巨大差异。这一特性使得攻击者难以通过猜测或穷举的方式来破解SM3算法。
SM4算法的安全性
SM4算法是一个分组密码算法,主要用于数据的加密和解密。其安全性主要体现在以下几个方面:
- 密钥长度:SM4算法的密钥长度为128比特,与AES算法相同。这一长度的密钥足以抵抗目前已知的所有密码攻击方法。
- 分组长度:SM4算法的分组长度也为128比特,这意味着每次加密或解密的数据块大小为128比特。这一分组长度可以确保数据的机密性和完整性。
- 加密轮数:SM4算法采用了多轮加密的方式,每轮加密都使用了不同的密钥和加密函数。这种加密方式可以使得攻击者难以通过分析加密过程来破解算法。
- 安全性评估:SM4算法已经经过了多轮的安全性评估和审查,其安全性得到了广泛的认可。目前,尚未有公开的针对SM4算法的有效攻击方法。
综上所述,国密算法中的SM2、SM3和SM4算法都具有较高的安全性,可以满足不同场景下的密码应用需求。在实际应用中,可以根据具体的需求和场景选择合适的算法进行使用。
在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);