【加密】SM4学习和使用

一、SM4 算法背景

  1. 国密算法
    SM4 是中国国家密码管理局(GM/T)于 2012 年发布的商用密码算法,属于 国密标准(GM/T 0002-2012),与 SM2(非对称加密)、SM3(哈希算法)共同构成中国自主密码体系。

    • 类型:对称分组加密算法。

    • 设计目标:替代国际算法(如 AES),满足金融、政务等领域的高安全性需求。


二、SM4 核心特性

特性说明
分组长度128 位(16 字节)
密钥长度128 位(16 字节)
加密模式支持 ECB、CBC、CTR、OFB 等模式(需自行实现或依赖密码库)
轮数32 轮非线性迭代
安全性抗差分攻击、线性攻击,符合金融级安全标准

三、SM4 算法流程

  1. 密钥扩展

    • 将 128 位初始密钥扩展为 32 个 32 位的轮密钥(rk[0] 到 rk[31])。

    • 轮密钥生成过程包含非线性变换和循环移位。

  2. 加密过程

    • 输入 128 位明文分组,通过 32 轮迭代操作,每轮包含:

      • 非线性变换(S盒):4 个并行的 8 位 S 盒替换。

      • 线性变换:循环移位和异或操作。

    • 最终输出 128 位密文。

  3. 解密过程

    • 解密与加密流程相同,但轮密钥需按逆序(rk[31] 到 rk[0])使用。


四、SM4 vs AES

特性SM4AES-128
标准中国国密标准(GM/T)国际标准(NIST)
分组长度128 位128 位
密钥长度128 位128/192/256 位
轮数32 轮10/12/14 轮(依密钥长度)
应用场景中国政务、金融、物联网国际通用领域

五、SM4 应用场景

  1. 数据加密

    • 数据库字段、文件、通信内容的加密保护。

    • 示例:敏感信息(如身份证号)存储时使用 SM4 加密。

  2. 金融支付

    • POS 终端、移动支付中的数据加密传输。

    • 示例:银联部分终端采用 SM4 加密交易信息。

  3. 物联网安全

    • 设备间的安全通信协议(如 TLS 中集成 SM4)。

  4. 国密合规场景

    • 满足中国密码法规要求的系统(如政务云、电子发票)。

代码示例:

import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Base64;

/**
 * <p>SM4 对称加密工具类(支持 ECB/CBC 模式)</p>
 *
 * @CreateTime: 2025-03-10 11:50 <br>
 */

public class SM4Utils {
	static {
		// 添加 BouncyCastle 支持
		Security.addProvider(new BouncyCastleProvider());
	}

	private static final String ALGORITHM = "SM4";
	/**
	 * SM4 密钥长度固定为 128 位
	 */
	private static final int KEY_SIZE = 128;
	/**
	 * CBC 模式初始化向量长度 16 字节
	 */
	private static final int IV_SIZE = 16;

	// -------------------- 密钥生成 ----------------------------------------

	/**
	 * 生成随机密钥(Base64 编码)
	 */
	public static String generateKeyBase64() {
		return Base64.getEncoder().encodeToString(generateKeyBytes());
	}

	/**
	 * 生成随机密钥(字节数组)
	 */
	public static byte[] generateKeyBytes() {
		SecureRandom random = new SecureRandom();
		byte[] key = new byte[KEY_SIZE / 8];
		random.nextBytes(key);
		return key;
	}

	/**
	 * 生成 CBC 模式所需的初始化向量(Base64 编码)
	 */
	public static String generateIvBase64() {
		return Base64.getEncoder().encodeToString(generateIvBytes());
	}

	/**
	 * 生成 CBC 模式所需的初始化向量(字节数组)
	 */
	public static byte[] generateIvBytes() {
		SecureRandom random = new SecureRandom();
		byte[] iv = new byte[IV_SIZE];
		random.nextBytes(iv);
		return iv;
	}

	// -------------------- 加密方法 ----------------------------------------

	/**
	 * ECB 模式加密(结果返回 Base64 字符串)
	 *
	 * @param plaintext 明文
	 * @param base64Key Base64 编码的密钥
	 */
	public static String encryptEcbBase64(String plaintext, String base64Key) {
		byte[] key = Base64.getDecoder().decode(base64Key);
		byte[] ciphertext = encryptEcb(plaintext.getBytes(StandardCharsets.UTF_8), key);
		return Base64.getEncoder().encodeToString(ciphertext);
	}

	/**
	 * CBC 模式加密(结果返回 Base64 字符串,包含 IV)
	 *
	 * @param plaintext 明文
	 * @param base64Key Base64 编码的密钥
	 * @param base64Iv  Base64 编码的初始化向量(若为 null 则自动生成)
	 */
	public static String encryptCbcBase64(String plaintext, String base64Key, String base64Iv) {
		byte[] key = Base64.getDecoder().decode(base64Key);
		byte[] iv = (base64Iv == null) ? generateIvBytes() : Base64.getDecoder().decode(base64Iv);
		byte[] ciphertext = encryptCbc(plaintext.getBytes(StandardCharsets.UTF_8), key, iv);
		// 将 IV 附加到密文前(IV 不加密,与密文拼接后返回)
		byte[] combined = new byte[iv.length + ciphertext.length];
		System.arraycopy(iv, 0, combined, 0, iv.length);
		System.arraycopy(ciphertext, 0, combined, iv.length, ciphertext.length);
		return Base64.getEncoder().encodeToString(combined);
	}

	/**
	 * ECB 模式加密(字节数组)
	 */
	public static byte[] encryptEcb(byte[] plaintext, byte[] key) {
		return process(Cipher.ENCRYPT_MODE, "SM4/ECB/PKCS5Padding", key, null, plaintext);
	}

	/**
	 * CBC 模式加密(字节数组)
	 */
	public static byte[] encryptCbc(byte[] plaintext, byte[] key, byte[] iv) {
		return process(Cipher.ENCRYPT_MODE, "SM4/CBC/PKCS5Padding", key, iv, plaintext);
	}

	// -------------------- 解密方法 ----------------------------------------

	/**
	 * ECB 模式解密(Base64 输入)
	 *
	 * @param ciphertextBase64 Base64 编码的密文
	 * @param base64Key        Base64 编码的密钥
	 */
	public static String decryptEcbBase64(String ciphertextBase64, String base64Key) {
		byte[] key = Base64.getDecoder().decode(base64Key);
		byte[] ciphertext = Base64.getDecoder().decode(ciphertextBase64);
		byte[] plaintext = decryptEcb(ciphertext, key);
		return new String(plaintext, StandardCharsets.UTF_8);
	}

	/**
	 * CBC 模式解密(Base64 输入,包含 IV)
	 *
	 * @param ciphertextBase64 Base64 编码的密文(前16字节为 IV)
	 * @param base64Key        Base64 编码的密钥
	 */
	public static String decryptCbcBase64(String ciphertextBase64, String base64Key) {
		byte[] key = Base64.getDecoder().decode(base64Key);
		byte[] combined = Base64.getDecoder().decode(ciphertextBase64);
		// 分离 IV 和密文
		byte[] iv = new byte[IV_SIZE];
		byte[] ciphertext = new byte[combined.length - IV_SIZE];
		System.arraycopy(combined, 0, iv, 0, IV_SIZE);
		System.arraycopy(combined, IV_SIZE, ciphertext, 0, ciphertext.length);
		byte[] plaintext = decryptCbc(ciphertext, key, iv);
		return new String(plaintext, StandardCharsets.UTF_8);
	}

	/**
	 * ECB 模式解密(字节数组)
	 */
	public static byte[] decryptEcb(byte[] ciphertext, byte[] key) {
		return process(Cipher.DECRYPT_MODE, "SM4/ECB/PKCS5Padding", key, null, ciphertext);
	}

	/**
	 * CBC 模式解密(字节数组)
	 */
	public static byte[] decryptCbc(byte[] ciphertext, byte[] key, byte[] iv) {
		return process(Cipher.DECRYPT_MODE, "SM4/CBC/PKCS5Padding", key, iv, ciphertext);
	}

	// -------------------- 核心加解密逻辑 --------------------

	private static byte[] process(int mode, String transformation, byte[] key, byte[] iv, byte[] data) {
		try {
			SecretKeySpec keySpec = new SecretKeySpec(key, ALGORITHM);
			Cipher cipher = Cipher.getInstance(transformation, BouncyCastleProvider.PROVIDER_NAME);
			if (iv == null) {
				cipher.init(mode, keySpec);
			} else {
				cipher.init(mode, keySpec, new IvParameterSpec(iv));
			}
			return cipher.doFinal(data);
		} catch (Exception e) {
			throw new RuntimeException("SM4 加解密失败: " + e.getMessage(), e);
		}
	}

	// -------------------- 测试示例 --------------------

	public static void main(String[] args) {
		// 1. 生成密钥和 IV
		String key = SM4Utils.generateKeyBase64();
		String iv = SM4Utils.generateIvBase64();
		String plaintext = "Hello, SM4测试!";

		// 2. ECB 模式加密/解密
		String ciphertextEcb = SM4Utils.encryptEcbBase64(plaintext, key);
		String decryptedEcb = SM4Utils.decryptEcbBase64(ciphertextEcb, key);
		System.out.println("ECB 模式加密结果: " + ciphertextEcb);
		System.out.println("ECB 模式解密结果: " + decryptedEcb);

		// 3. CBC 模式加密/解密(自动生成 IV)
		String ciphertextCbc = SM4Utils.encryptCbcBase64(plaintext, key, null);
		String decryptedCbc = SM4Utils.decryptCbcBase64(ciphertextCbc, key);
		System.out.println("CBC 模式加密结果: " + ciphertextCbc);
		System.out.println("CBC 模式解密结果: " + decryptedCbc);
	}
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值