赞
踩
不知不觉都2023年5月份了,这一期也是国密系列的最后一期,SM4对称加密算法的四种语言互通演示。第一期博主主要演示了java、C#、nodejs、golang4种开发语言的sm2密钥对生成的代码,传送门:java国密 C#国密 golang国密 NodeJS国密汇总(一),第二期博主主要演示了java、C#、nodejs、golang4种开发语言的sm2加签验签、加密解密的代码,传送门:java国密 C#国密 golang国密 NodeJS国密汇总(二),第三期博主主要演示了java、C#、nodejs、golang4种开发语言的sm3杂凑算法的代码,传送门:java国密 C#国密 golang国密 NodeJS国密汇总(三)。回顾完后博主就开始本期的内容演示,本期主要把四种开发语言的SM4对称加密算法整上,希望对各位观众大佬有所帮助。
在SM4算法中,有两种常见的分组密码工作模式,ECB(Electronic Codebook)模式和CBC(Cipher Block Chaining)模式。
ECB模式
ECB模式是最简单的分组密码工作模式之一。它将明文划分为若干个固定大小的块,然后对每个块进行加密操作。由于每个块都独立加密,因此它能够很好地利用并行计算的优势,并且实现起来也比较简单。但是,ECB模式容易受到重放攻击和密码分析等攻击,因此在实际应用中不常使用。
CBC模式
CBC模式是常用的分组密码工作模式之一。它将每个块与前一个块的密文进行异或运算(称为“链接”)。在加密时,需要提供一个随机的初始化向量(IV)来作为第一个块的密文输入。因为新的每个块都依赖于前一个块的密文,所以CBC模式不易受到重放攻击和密码分析等攻击。但是,由于它需要保留最后一个块的密文作为下一个消息的IV,因此在流加密中不适用,而且它的性能比ECB模式稍差。
另外在SM4算法中,还有填充方式padding的区别,常用的填充方式有以下几种:
NoPadding
NoPadding是不进行任何填充的填充方式。当明文长度不是块大小的整数倍时,需要手动补齐。这种填充方式适用于对于输入明文的长度是预先定好的,而且需要自己手动补全的情况。相比其他填充方式,NoPadding不会增加数据包大小,但它存在泄漏明文长度的安全隐患。
PKCS5Padding/PKCS7Padding
PKCS5Padding和PKCS7Padding都是基于块大小进行填充的填充方式。在PKCS5Padding中,要求填充字节的值等于填充字节数量。例如,需要填充3个字节时,就填充3个字节的值为0x03。PKCS7Padding在PKCS5Padding的基础上增加了对块大小大于8位的支持。两种填充方式在功能上是一致的,只是在实现上略微有些不同。
ZeroPadding
ZeroPadding是用0x00字节填充明文的填充方式。这种填充方式易于实现,但可能泄露明文长度信息。
所以分组工作模式和填充方式需要一致,否则不同语言之间可能会出现无法解密或者只能解密乱码等奇怪现象。
本博客中只演示CBC模式和PKCS5Padding/PKCS7Padding填充方式,其他方式暂不演示,后续有时间的话会在补充。如果有读者想知道可以私信或在评论区留言。
基于第一期中1.64版本的bcprov依赖包,如果使用依赖包中自带的SM4Engine实现sm4算法,则默认的分组模式是ECB,默认的填充模式是NoPadding,这会导致如果明文不是16的整数倍,则加解密有一定几率出问题,各位可将示例中padding代码注释后自行测试。如果使用hutool或者其他工具包,可能默认的填充方式也会变成PKCS5Padding/PKCS7Padding,博主在没有引用工具包的情况下演示PKCS5Padding/PKCS7Padding如何实现,方便各位研究探讨。
代码示例:
package sm4; import org.bouncycastle.crypto.engines.SM4Engine; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.util.encoders.Base64; import org.bouncycastle.util.encoders.Hex; import java.nio.charset.StandardCharsets; /** * @author 系统异常 * @version 1.0 * @date 2023/5/2 17:31 * @description sm4对称解密算法演示 */ public class SM4Test { public static void main(String[] args) { // sm4密钥 String sm4key = "0123456789abcdef"; // 明文,添加了中文和标点符号和特殊字符 String plaintext = "你好,世界 hello world ,./;'<>?:\"!@#$%^&*()_+-=1234567890`"; System.out.println("java sm4 原文 -->" + plaintext); // 加密后编码格式 String encode = "hex"; String encryptStr = encrypt(plaintext, sm4key, encode); System.out.println("java sm4 PKCS7Padding 加密结果 -->" + encryptStr); String decryptStr = decrypt(encryptStr, sm4key, encode); System.out.println("java sm4 PKCS7Padding 解密结果 -->" + decryptStr); encode = "base64"; encryptStr = encrypt(plaintext, sm4key, encode); System.out.println("java sm4 PKCS7Padding 加密结果 -->" + encryptStr); decryptStr = decrypt(encryptStr, sm4key, encode); System.out.println("java sm4 PKCS7Padding 解密结果 -->" + decryptStr); } /** * sm4加密 * * @param plaintext 明文 * @param sm4key sm4密钥 * @param encode 密文编码格式 * @return 加密后结果 */ public static String encrypt(String plaintext, String sm4key, String encode) { byte[] input = plaintext.getBytes(StandardCharsets.UTF_8); // 加密前明文填充,PKCS7Padding byte[] ret; int p = 16 - input.length % 16; ret = new byte[input.length + p]; System.arraycopy(input, 0, ret, 0, input.length); for (int i = 0; i < p; i++) { ret[input.length + i] = (byte) p; } // sm4加密引擎 SM4Engine sm4Engine = new SM4Engine(); // 加密时候第一个参数为true sm4Engine.init(true, new KeyParameter(sm4key.getBytes(
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。