java集成国密SM4

1 加密信息介绍

在项目中有接触到使用国密SM4进行数据加密,因此整理一份java实现国密SM4的文档;

关于SM4是对称加密算法,因此只需要一个密钥即可;根据不同的工作模式,需要提供额外的参数;SM4加密存在的工作模式有:

  • ECB(Electronic Codebook):最基础的加密模式,每个数据块独立加密,相同明文块加密后得到相同的密文块。这种模式不提供数据之间的 diffusion(扩散),安全性相对较低,容易受到模式识别攻击。
  • CBC(Cipher Block Chaining):每个数据块在加密前会与前一个加密块的密文进行XOR运算,增加了数据块间的依赖性,提高了安全性。需要一个随机的初始化向量(IV)以保证每次加密的唯一性。
  • CFB(Cipher Feedback):每个数据块在加密前会与前一个加密块的密文进行XOR运算,增加了数据块间的依赖性,提高了安全性。需要一个随机的初始化向量(IV)以保证每次加密的唯一性。
  • OFB(Output Feedback):将加密块的输出(而非密文)与明文进行XOR,生成密文。同样使用IV,并且加密操作与数据流解耦,可以并行处理。
  • CTR(Counter):使用一个计数器(通常是初始向量加上一个递增的计数器值)与密钥加密,得到的输出与明文异或产生密文。此模式支持并行计算,安全性高且效率好。
  • GCM(Galois/Counter Mode):结合了CTR模式的高效性和认证加密(AEAD,Authenticated Encryption with Associated Data),提供了数据的机密性、完整性校验和认证。需要一个128位的nonce(随机数)和可选的附加数据。
  • CCM(Counter with CBC-MAC):同样是AEAD模式,结合了CTR模式加密和CBC-MAC消息认证码,适用于资源有限的环境。

综上所述,选择安全系数较高的的工作模式:GCM工作模式

2 代码示例

2.1 使用注意事项

  • 使用方法,请查看类中的main方法

2.2 代码实现

  • 使用国密SM4需要依赖三方的包bouncycastle-bcprov-jdk15on,以maven项目为示例,
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15on</artifactId>
    <version>1.70</version>
</dependency>
  • 下面的具体的实现代码
package com.panape.sm3demo.utils;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

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

public class SM4Util {
    /**
     * 加密秘钥,长度必须为16位
     */
    private static final String KEY = "1234567890abcdef";
    /**
     * 初始化向量
     */
    private static final String IV = "qwertyuiopas2356";
    /**
     * 加密算法
     */
    public static final String ALGORITHM = "SM4";
    /**
     * 加密工作模式:GCM
     * 数据填充模式:PKCS5Padding
     */
    public static final String ALGORITHM_MODEL_GCM_PADDING = "SM4/GCM/NoPadding";
    /**
     * 随机数的长度
     */
    public static final int NONCE_LENGTH = 128;

    static {
        // 添加Bouncy Castle provider
        Security.addProvider(new BouncyCastleProvider());
    }

    /**
     * 使用传输的密钥和初始化向量进行加密
     *
     * @param plainText 需要加密的内容
     * @return 加密之后的内容
     * @throws Exception 加密过程中可能发生的异常
     */
    public static String encryptWithGCM(String plainText, String key, String iv) throws Exception {
        return encryptWithGCM(plainText, key.getBytes(), iv.getBytes());
    }

    /**
     * 使用传输的密钥和初始化向量进行加密
     *
     * @param plainText 需要加密的内容
     * @param key       密钥
     * @param iv        初始化向量
     * @return 加密之后的内容
     * @throws Exception 加密过程中可能发生的异常
     */
    public static String decryptWithGCM(String plainText, String key, String iv) throws Exception {
        return decryptWithGCM(plainText, key.getBytes(), iv.getBytes());
    }

    /**
     * 使用配置的密钥和初始化向量进行加密
     *
     * @param plainText 需要加密的内容
     * @return 加密之后的内容
     * @throws Exception 加密过程中可能发生的异常
     */
    public static String encryptWithGCM(String plainText) throws Exception {
        return encryptWithGCM(plainText, KEY.getBytes(), IV.getBytes());
    }

    /**
     * 使用配置的密钥和初始化向量进行解密
     *
     * @param cipherText 需要解密的内容
     * @return 解密之后的内容
     * @throws Exception 解密过程中可能发生的异常
     */
    public static String decryptWithGCM(String cipherText) throws Exception {
        return decryptWithGCM(cipherText, KEY.getBytes(), IV.getBytes());
    }

    /**
     * 使用SM4-GCM模式加密
     *
     * @param plainText 需要加密的内容
     * @param keyBytes  密钥字节数组
     * @param ivBytes   初始化向量字节数组
     * @return 加密之后的内容
     * @throws Exception 加密过程中可能发生的异常
     */
    public static String encryptWithGCM(String plainText, byte[] keyBytes, byte[] ivBytes) throws Exception {
        SecretKeySpec sm4Key = new SecretKeySpec(keyBytes, ALGORITHM);
        GCMParameterSpec ivSpec = new GCMParameterSpec(NONCE_LENGTH, ivBytes);

        Cipher cipher = Cipher.getInstance(ALGORITHM_MODEL_GCM_PADDING, "BC");
        cipher.init(Cipher.ENCRYPT_MODE, sm4Key, ivSpec);
        byte[] encryptedBytes = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(encryptedBytes);
    }

    /**
     * 使用SM4-GCM模式解密
     *
     * @param cipherText 需要解密的内容
     * @param keyBytes   密钥字节数组
     * @param ivBytes    初始化向量字节数据
     * @return 解密之后内容
     * @throws Exception 解密过程中可能发生的异常
     */
    public static String decryptWithGCM(String cipherText, byte[] keyBytes, byte[] ivBytes) throws Exception {
        SecretKeySpec sm4Key = new SecretKeySpec(keyBytes, ALGORITHM);
        GCMParameterSpec ivSpec = new GCMParameterSpec(NONCE_LENGTH, ivBytes);

        Cipher cipher = Cipher.getInstance(ALGORITHM_MODEL_GCM_PADDING, "BC");
        cipher.init(Cipher.DECRYPT_MODE, sm4Key, ivSpec);
        byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(cipherText));
        return new String(decryptedBytes, StandardCharsets.UTF_8);
    }

    /**
     * 使用SecureRandom生成指定长度的密钥或IV
     *
     * @param length 密钥或IV的长度(字节数)
     * @return 生成的随机字节数组
     */
    public static byte[] generateKey(int length) {
        SecureRandom secureRandom = new SecureRandom();
        byte[] bytes = new byte[length];
        secureRandom.nextBytes(bytes);
        return bytes;
    }

    /**
     * 生成指定长度的初始化向量(IV)
     *
     * @param length IV的长度(字节数)
     * @return 生成的随机字节数组
     */
    public static byte[] generateIV(int length) {
        // IV的生成方式与密钥相同,使用SecureRandom
        return generateKey(length);
    }

    public static void main(String[] args) throws Exception {
        System.out.println("使用配置的密钥和初始化向量");
        String plainText = "1234567";
        String cipherText = encryptWithGCM(plainText);
        System.out.println("加密后:" + cipherText);

        String decryptedText = decryptWithGCM(cipherText);
        System.out.println("解密后:" + decryptedText);

        System.out.println();
        System.out.println("使用代码随机生成的密钥和初始化向量");
        // 使用随机的
        byte[] key = generateKey(16);
        byte[] iv = generateIV(16);
        String cipherText1 = encryptWithGCM(plainText, key, iv);
        System.out.println("加密后:" + cipherText1);

        String decryptedText1 = decryptWithGCM(cipherText1, key, iv);
        System.out.println("解密后:" + decryptedText1);

        System.out.println();
        System.out.println("使用外部传输的密钥和初始化向量");
        String cipherText2 = encryptWithGCM(plainText, KEY, IV);
        System.out.println("加密后:" + cipherText2);

        String decryptedText2 = decryptWithGCM(cipherText2, KEY, IV);
        System.out.println("解密后:" + decryptedText2);
    }
}
  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值