国密SM4加密Java实现

国密SM4加密Java实现代码:

package com.vfei.common.util;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

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

/**
 * 国密SM4加密工具类
 * SM4算法是中国国家密码管理局发布的对称加密算法
 * 密钥长度:128位(16字节)
 * 分组长度:128位(16字节)
 * 加密模式:CBC
 * 填充方式:PKCS5Padding
 */
public class SM4Encrypt {

    /**
     * 密钥空间大小(128位)
     */
    public static final int SM4_KEY_SIZE = 128;

    /**
     * 密钥长度(16字节)
     */
    public static final int SM4_KEY_LENGTH = 16;

    /**
     * 算法名称
     */
    public static final String SM4_NAME = "SM4";

    /**
     * CBC模式 + PKCS5填充
     */
    public static final String SM4_CBC_PKCS5 = "SM4/CBC/PKCS5Padding";

    /**
     * 默认初始向量(16字节)
     */
    protected static final byte[] SM4_IV = {1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31};

    static {
        // 注册BouncyCastle安全提供者
        Security.addProvider(new BouncyCastleProvider());
    }

    /**
     * 生成SM4密钥(Base64格式)
     *
     * @return Base64编码的密钥
     */
    public String generateSm4Key() {
        try {
            return Base64.getEncoder().encodeToString(generateSm4KeyBytes(SM4_KEY_SIZE));
        } catch (NoSuchAlgorithmException | NoSuchProviderException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 生成SM4密钥字节数组
     *
     * @param keySize 密钥空间大小
     * @return 密钥字节数组
     */
    private static byte[] generateSm4KeyBytes(int keySize) throws NoSuchAlgorithmException, NoSuchProviderException {
        KeyGenerator keyGenerator = KeyGenerator.getInstance(SM4_NAME, BouncyCastleProvider.PROVIDER_NAME);
        keyGenerator.init(keySize, new SecureRandom());
        return keyGenerator.generateKey().getEncoded();
    }

    /**
     * 文本加密(CBC模式)
     *
     * @param plainText 明文
     * @param sm4Key    Base64编码的密钥
     * @return Base64编码的密文
     */
    public String encryptText(String plainText, String sm4Key) {
        try {
            byte[] keyBytes = prepareKey(sm4Key);
            byte[] dataBytes = plainText.getBytes(StandardCharsets.UTF_8);
            byte[] encryptedBytes = encryptCbc(dataBytes, keyBytes, SM4_IV);
            return Base64.getEncoder().encodeToString(encryptedBytes);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 文本解密(CBC模式)
     *
     * @param cipherText Base64编码的密文
     * @param sm4Key     Base64编码的密钥
     * @return 明文
     */
    public String decryptText(String cipherText, String sm4Key) {
        try {
            byte[] keyBytes = prepareKey(sm4Key);
            byte[] encryptedBytes = Base64.getDecoder().decode(cipherText);
            byte[] decryptedBytes = decryptCbc(encryptedBytes, keyBytes, SM4_IV);
            return new String(decryptedBytes, StandardCharsets.UTF_8);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 字节数组加密(CBC模式)
     *
     * @param plainBytes 明文字节数组
     * @param keyBytes   密钥字节数组
     * @param iv         初始向量
     * @return 密文字节数组
     */
    public byte[] encryptCbc(byte[] plainBytes, byte[] keyBytes, byte[] iv) throws Exception {
        SecretKeySpec keySpec = new SecretKeySpec(keyBytes, SM4_NAME);
        IvParameterSpec ivSpec = new IvParameterSpec(iv);
        
        Cipher cipher = Cipher.getInstance(SM4_CBC_PKCS5, BouncyCastleProvider.PROVIDER_NAME);
        cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
        
        return cipher.doFinal(plainBytes);
    }

    /**
     * 字节数组解密(CBC模式)
     *
     * @param cipherBytes 密文字节数组
     * @param keyBytes    密钥字节数组
     * @param iv          初始向量
     * @return 明文字节数组
     */
    public byte[] decryptCbc(byte[] cipherBytes, byte[] keyBytes, byte[] iv) throws Exception {
        SecretKeySpec keySpec = new SecretKeySpec(keyBytes, SM4_NAME);
        IvParameterSpec ivSpec = new IvParameterSpec(iv);
        
        Cipher cipher = Cipher.getInstance(SM4_CBC_PKCS5, BouncyCastleProvider.PROVIDER_NAME);
        cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
        
        return cipher.doFinal(cipherBytes);
    }

    /**
     * 准备密钥(处理并解码)
     *
     * @param sm4Key Base64编码的密钥
     * @return 密钥字节数组
     */
    private byte[] prepareKey(String sm4Key) {
        // 密钥长度处理
        sm4Key = keyPadding(sm4Key);
        // Base64解码
        return Base64.getDecoder().decode(sm4Key);
    }

    /**
     * 密钥长度补齐处理
     * 确保密钥长度符合SM4要求(16字节的Base64编码约24字符)
     *
     * @param key 原始密钥
     * @return 处理后的密钥
     */
    private String keyPadding(String key) {
        if (key == null) {
            throw new IllegalArgumentException("密钥不能为空");
        }
        // 可根据需要实现密钥长度校验和补齐逻辑
        return key;
    }

    /**
     * 测试方法
     */
    public static void main(String[] args) {
        SM4Encrypt encrypt = new SM4Encrypt();
        
        // 生成密钥
        String key = encrypt.generateSm4Key();
        System.out.println("生成的密钥: " + key);
        
        // 待加密数据
        String plainText = "{\"phone\":\"13800138000\",\"name\":\"张三\"}";
        System.out.println("明文: " + plainText);
        
        // 加密
        String encrypted = encrypt.encryptText(plainText, key);
        System.out.println("加密后: " + encrypted);
        
        // 解密
        String decrypted = encrypt.decryptText(encrypted, key);
        System.out.println("解密后: " + decrypted);
    }
}

核心要点说明

要素说明
算法标准GB/T 32907-2016 《信息安全技术 SM4分组密码算法》
密钥长度固定128位(16字节)
分组长度128位(16字节)
加密模式CBC (Cipher Block Chaining)
填充方式PKCS5Padding
初始向量固定16字节数组
依赖库BouncyCastle (bcprov-jdk18on)

Maven依赖

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk18on</artifactId>
    <version>1.76</version>
</dependency>

安全注意事项

  1. 密钥管理:密钥必须妥善保管,不能硬编码在代码中
  2. 初始向量(IV):每次加密应使用不同的IV,增强安全性
  3. 密钥派生:建议使用密钥派生函数(KDF)从密码生成密钥
  4. 异常处理:加密解密失败时应妥善处理异常,避免敏感信息泄露
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值