前言
在现代信息安全领域,数据加密技术是保障数据安全的核心手段之一。SM4作为中国国家密码管理局发布的对称加密算法,因其高效性和安全性,广泛应用于金融、政务、通信等领域。本文将详细介绍如何使用Java实现SM4的加解密操作,并深入探讨SM4的几种常见加密模式及其原理。
一、结果验证
1.代码运行结果
1.1 ECB模式加密结果

1.2 CBC模式加密结果

2.工具运行结果
2.1 ECB模式解密结果
2.2 CBC模式解密结果

二、SM4算法简介
SM4是一种分组密码算法,分组长度和密钥长度均为128位(16字节)。它采用与AES类似的轮函数结构,但具体的S盒和线性变换与AES不同,因此具有独特的加密性能。SM4支持多种加密模式,包括ECB、CBC、CTR、CFB和OFB等,以适应不同的应用场景。
三、SM4加密模式详解
1. ECB模式(电子密码本模式)
原理:
ECB模式是最简单的加密模式,它将明文分成若干个固定大小的块(如128位),然后对每个块独立进行加密。由于每个块的加密是独立的,相同的明文块会被加密成相同的密文块。
优点:
- 实现简单,易于并行处理。
缺点:
- 安全性较低,相同的明文块会产生相同的密文块,容易被分析。
- 不适合加密包含重复模式的数据,如图像或文本。
适用场景:
- 适用于加密随机数据或不需要高安全性的场景。
2. CBC模式(加密分组链接模式)
原理:
CBC模式通过引入初始向量(IV)和分组链接机制,解决了ECB模式的安全性问题。每个明文分组在加密前会与前一个密文分组进行异或操作,然后再进行加密。第一个分组使用初始向量(IV)进行异或操作。
优点:
- 相同的明文块在不同的位置会被加密成不同的密文块,安全性较高。
- 适合加密包含重复模式的数据。
缺点:
- 加密过程是串行的,不能并行处理。
- 需要管理和传输初始向量(IV)。
适用场景:
- 适用于需要较高安全性的场景,如文件加密、网络通信加密等。
3. CTR模式(计数器模式)
原理:
CTR模式将计数器的值与密钥结合生成伪随机序列,然后与明文进行异或操作生成密文。计数器可以是任意值,但必须保证唯一性。通常,计数器会从某个初始值开始递增。
优点:
- 加密和解密过程可以并行处理,效率较高。
- 不需要填充数据,适合加密任意长度的数据。
缺点:
- 计数器的管理较为复杂,必须保证唯一性。
- 如果计数器重复使用,会导致安全性问题。
适用场景:
- 适用于需要高效加密的场景,如实时通信、流媒体加密等。
4. CFB模式(密码反馈模式)
原理:
CFB模式将密文分组的一部分反馈到加密过程中,通过与明文异或生成密文。CFB模式可以将分组密码转换为自同步的流密码。每个分组的加密依赖于前一个分组的密文。
优点:
- 适合处理小数据块,具有良好的错误传播特性。
- 可以实现自同步,适合传输过程中可能出现数据丢失的场景。
缺点:
- 加密过程是串行的,不能并行处理。
- 需要管理和传输初始向量(IV)。
适用场景:
- 适用于需要自同步特性的场景,如实时通信、流媒体加密等。
5. OFB模式(输出反馈模式)
原理:
OFB模式将加密器的输出反馈到输入中,生成伪随机序列,然后与明文异或生成密文。OFB模式与CTR模式类似,但它更适合处理小数据块。
优点:
- 加密和解密过程可以并行处理,效率较高。
- 不需要填充数据,适合加密任意长度的数据。
缺点:
- 如果初始向量(IV)重复使用,会导致安全性问题。
- 加密过程是串行的,不能并行处理。
适用场景:
- 适用于需要高效加密的场景,如实时通信、流媒体加密等。
四、Java实现SM4加解密
1. 引入依赖
在pom.xml
中添加以下依赖:
xml
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.65</version>
</dependency>
2. 实现代码
java
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Hex;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.Security;
import java.util.Base64;
public class SM4EncDec {
static {
// 添加Bouncy Castle提供者
Security.addProvider(new BouncyCastleProvider());
}
// 算法名称
public static final String ALGORITHM_NAME = "SM4";
public static final String ALGORITHM_NAME_ECB_PADDING = "SM4/ECB/PKCS7Padding";
public static final String ALGORITHM_NAME_CBC_PADDING = "SM4/CBC/PKCS7Padding";
/**
* 生成128位密钥
*/
public static String generateKey() {
byte[] key = new byte[16]; // 128位密钥
new java.security.SecureRandom().nextBytes(key);
return Hex.toHexString(key);
}
/**
* ECB模式加密
*
* @param keyHex 16字节密钥(Hex格式)
* @param data 明文数据
* @return 加密后的Hex字符串
*/
public static String encryptECB(String keyHex, String data) throws Exception {
byte[] keyData = Hex.decode(keyHex);
byte[] dataBytes = data.getBytes("UTF-8");
// 初始化Cipher
Cipher cipher = Cipher.getInstance(ALGORITHM_NAME_ECB_PADDING, "BC");
SecretKeySpec keySpec = new SecretKeySpec(keyData, ALGORITHM_NAME);
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
// 加密
byte[] encrypted = cipher.doFinal(dataBytes);
return Hex.toHexString(encrypted);
}
/**
* ECB模式解密
*
* @param keyHex 16字节密钥(Hex格式)
* @param cipherDataHex 密文数据(Hex格式)
* @return 解密后的字符串
*/
public static String decryptECB(String keyHex, String cipherDataHex) throws Exception {
byte[] keyData = Hex.decode(keyHex);
byte[] cipherData = Hex.decode(cipherDataHex);
// 初始化Cipher
Cipher cipher = Cipher.getInstance(ALGORITHM_NAME_ECB_PADDING, "BC");
SecretKeySpec keySpec = new SecretKeySpec(keyData, ALGORITHM_NAME);
cipher.init(Cipher.DECRYPT_MODE, keySpec);
// 解密
byte[] decrypted = cipher.doFinal(cipherData);
return new String(decrypted, "UTF-8");
}
/**
* CBC模式加密
*
* @param keyHex 16字节密钥(Hex格式)
* @param data 明文数据
* @param ivHex 16字节初始向量(Hex格式)
* @return 加密后的Hex字符串
*/
public static String encryptCBC(String keyHex, String data, String ivHex) throws Exception {
byte[] keyData = Hex.decode(keyHex);
byte[] dataBytes = data.getBytes("UTF-8");
byte[] ivBytes = Hex.decode(ivHex);
// 初始化Cipher
Cipher cipher = Cipher.getInstance(ALGORITHM_NAME_CBC_PADDING, "BC");
SecretKeySpec keySpec = new SecretKeySpec(keyData, ALGORITHM_NAME);
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
// 加密
byte[] encrypted = cipher.doFinal(dataBytes);
return Hex.toHexString(encrypted);
}
/**
* CBC模式解密
*
* @param keyHex 16字节密钥(Hex格式)
* @param cipherDataHex 密文数据(Hex格式)
* @param ivHex 16字节初始向量(Hex格式)
* @return 解密后的字符串
*/
public static String decryptCBC(String keyHex, String cipherDataHex, String ivHex) throws Exception {
byte[] keyData = Hex.decode(keyHex);
byte[] cipherData = Hex.decode(cipherDataHex);
byte[] ivBytes = Hex.decode(ivHex);
// 初始化Cipher
Cipher cipher = Cipher.getInstance(ALGORITHM_NAME_CBC_PADDING, "BC");
SecretKeySpec keySpec = new SecretKeySpec(keyData, ALGORITHM_NAME);
IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
// 解密
byte[] decrypted = cipher.doFinal(cipherData);
return new String(decrypted, "UTF-8");
}
public static void main(String[] args) throws Exception {
// 生成密钥
String key = generateKey();
System.out.println("生成的密钥: " + key);
String plainText = "Hello, SM4!";
System.out.println("明文: " + plainText);
// ECB模式加密解密
String cipherTextECB = encryptECB(key, plainText);
System.out.println("ECB模式加密结果: " + cipherTextECB);
String decryptedTextECB = decryptECB(key, cipherTextECB);
System.out.println("ECB模式解密结果: " + decryptedTextECB);
// CBC模式加密解密
String iv = "1234567812345678"; // 初始向量
String cipherTextCBC = encryptCBC(key, plainText, iv);
System.out.println("CBC模式加密结果: " + cipherTextCBC);
String decryptedTextCBC = decryptCBC(key, cipherTextCBC, iv);
System.out.println("CBC模式解密结果: " + decryptedTextCBC);
}
}
五、总结
在实际项目中常用CBC模式,ECB模式安全性较低。密评时ECB模式会要求整改为CBC模式。
如果觉得不错,记得点赞收藏哦!