首页 > 编程语言 >【Java】SM4Utils(国密 SM4 工具类)

【Java】SM4Utils(国密 SM4 工具类)

时间:2023-08-25 10:33:08浏览次数:65  
标签:Java String SM4 padding 国密 static key return data

基于 bouncycastle 实现 国密 SM4

<!-- 引入 bouncycastle -->
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15on</artifactId>
    <version>1.70</version>
</dependency>
import lombok.Getter;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.encoders.Hex;

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.SecureRandom;
import java.security.Security;
import java.util.Base64;

import static java.util.Objects.isNull;

@Slf4j
public class SM4Utils {

    private static final int DEFAULT_KEY_SIZE = 128;
    private static final String ALGORITHM = "SM4";
    private static final String SM4_ECB_ = "SM4/ECB/";
    private static final String SM4_CBC_ = "SM4/CBC/";
    private static final Base64.Encoder BASE64_ENCODER = Base64.getEncoder();
    private static final Base64.Decoder BASE64_DECODER = Base64.getDecoder();
    private static final BouncyCastleProvider PROVIDER = new BouncyCastleProvider();

    static {
        if (isNull(Security.getProvider(BouncyCastleProvider.PROVIDER_NAME))) {
            Security.addProvider(PROVIDER);
        }
    }

    @Getter
    public enum Padding {
        PKCS5("PKCS5Padding"),
        PKCS7("PKCS7Padding"),
        ISO10126("ISO10126Padding");

        private final String name;

        Padding(String name) {
            this.name = name;
        }
    }

    // region generateKey

    public static byte[] genKey() {
        return genKey(DEFAULT_KEY_SIZE);
    }

    @SneakyThrows
    public static byte[] genKey(int keySize) {
        KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM, BouncyCastleProvider.PROVIDER_NAME);
        kg.init(keySize, new SecureRandom());
        return kg.generateKey().getEncoded();
    }

    public static String genKeyAsHex() {
        return genKeyAsHex(DEFAULT_KEY_SIZE);
    }

    public static String genKeyAsHex(int keySize) {
        return Hex.toHexString(genKey(keySize));
    }

    public static String genKeyAsBase64() {
        return genKeyAsBase64(DEFAULT_KEY_SIZE);
    }

    public static String genKeyAsBase64(int keySize) {
        return BASE64_ENCODER.encodeToString(genKey(keySize));
    }

    // endregion generateKey

    // region ECB mode

    @SneakyThrows
    public static Cipher getCipher_ECB(Padding padding) {
        return Cipher.getInstance(SM4_ECB_ + padding.name, BouncyCastleProvider.PROVIDER_NAME);
    }

    /**
     * 使用指定的加密算法和密钥对给定的字节数组进行加密
     *
     * @param data 要加密的字节数组
     * @param key  加密所需的密钥
     * @return byte[]   加密后的字节数组
     */
    @SneakyThrows
    public static byte[] encrypt_ECB(byte[] data, byte[] key, Padding padding) {
        Cipher cipher = getCipher_ECB(padding);
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
        return cipher.doFinal(data);
    }

    /**
     * 使用指定的加密算法和密钥对给定的字节数组进行解密
     *
     * @param data 要解密的字节数组
     * @param key  解密所需的密钥
     * @return byte[]   解密后的字节数组
     */
    @SneakyThrows
    public static byte[] decrypt_ECB(byte[] data, byte[] key, Padding padding) {
        Cipher cipher = getCipher_ECB(padding);
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
        return cipher.doFinal(data);
    }

    public static String encryptHex_ECB(String data, String key, Padding padding) {
        return Hex.toHexString(encrypt_ECB(
                data.getBytes(StandardCharsets.UTF_8),
                Hex.decode(key),
                padding
        ));
    }

    public static String decryptHex_ECB(String data, String key, Padding padding) {
        return new String(decrypt_ECB(
                Hex.decode(data),
                Hex.decode(key),
                padding
        ), StandardCharsets.UTF_8);
    }

    public static String encryptBase64_ECB(String data, String key, Padding padding) {
        return BASE64_ENCODER.encodeToString(encrypt_ECB(
                data.getBytes(StandardCharsets.UTF_8),
                BASE64_DECODER.decode(key),
                padding
        ));
    }

    public static String decryptBase64_ECB(String data, String key, Padding padding) {
        return new String(decrypt_ECB(
                BASE64_DECODER.decode(data),
                BASE64_DECODER.decode(key),
                padding
        ), StandardCharsets.UTF_8);
    }

    // endregion ECB mode

    // region CBC mode

    @SneakyThrows
    public static Cipher getCipher_CBC(Padding padding) {
        return Cipher.getInstance(SM4_CBC_ + padding.name, BouncyCastleProvider.PROVIDER_NAME);
    }

    /**
     * 使用指定的加密算法和密钥对给定的字节数组进行加密
     *
     * @param data 要加密的字节数组
     * @param key  加密所需的密钥
     * @param iv   解密所需的 IV
     * @return byte[]   加密后的字节数组
     */
    @SneakyThrows
    public static byte[] encrypt_CBC(byte[] data, byte[] key, byte[] iv, Padding padding) {
        Cipher cipher = getCipher_CBC(padding);
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, ALGORITHM);
        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
        return cipher.doFinal(data);
    }

    /**
     * 使用指定的加密算法和密钥对给定的字节数组进行解密
     *
     * @param data 要解密的字节数组
     * @param key  解密所需的密钥
     * @param iv   解密所需的 IV
     * @return byte[]   解密后的字节数组
     */
    @SneakyThrows
    public static byte[] decrypt_CBC(byte[] data, byte[] key, byte[] iv, Padding padding) {
        Cipher cipher = getCipher_CBC(padding);
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(iv));
        return cipher.doFinal(data);
    }

    public static String encryptHex_CBC(String data, String key, String iv, Padding padding) {
        return Hex.toHexString(encrypt_CBC(
                data.getBytes(StandardCharsets.UTF_8),
                Hex.decode(key),
                Hex.decode(iv),
                padding
        ));
    }

    public static String decryptHex_CBC(String data, String key, String iv, Padding padding) {
        return new String(decrypt_CBC(
                Hex.decode(data),
                Hex.decode(key),
                Hex.decode(iv),
                padding
        ), StandardCharsets.UTF_8);
    }

    public static String encryptBase64_CBC(String data, String key, String iv, Padding padding) {
        return BASE64_ENCODER.encodeToString(encrypt_CBC(
                data.getBytes(StandardCharsets.UTF_8),
                BASE64_DECODER.decode(key),
                BASE64_DECODER.decode(iv),
                padding
        ));
    }

    public static String decryptBase64_CBC(String data, String key, String iv, Padding padding) {
        return new String(decrypt_CBC(
                BASE64_DECODER.decode(data),
                BASE64_DECODER.decode(key),
                BASE64_DECODER.decode(iv),
                padding
        ), StandardCharsets.UTF_8);
    }

    // endregion CBC mode

    public static void main(String[] args) {
//        final Padding padding = Padding.PKCS5;
//        final String plainText = "123456";
//        final byte[] iv = "0123456789abcdef".getBytes(StandardCharsets.UTF_8);
//
//        final String key0 = genKeyAsBase64();
//        log.debug("\n密钥 : {}", key0);
//
//        String encrypt0 = encryptBase64_ECB(plainText, key0, padding);
//        String decrypt0 = decryptBase64_ECB(encrypt0, key0, padding);
//        log.debug("\n加密 : {}\n解密 : {}", encrypt0, decrypt0);
//
//        String encrypt1 = encryptBase64_CBC(plainText, key0, BASE64_ENCODER.encodeToString(iv), padding);
//        String decrypt1 = decryptBase64_CBC(encrypt1, key0, BASE64_ENCODER.encodeToString(iv), padding);
//        log.debug("\n加密 : {}\n解密 : {}", encrypt1, decrypt1);
//
//        final String key1 = genKeyAsHex();
//        log.debug("\n密钥 : {}", key1);
//
//        String encrypt2 = encryptHex_ECB(plainText, key1, padding);
//        String decrypt2 = decryptHex_ECB(encrypt2, key1, padding);
//        log.debug("\n加密 : {}\n解密 : {}", encrypt2, decrypt2);
//
//        String encrypt3 = encryptHex_CBC(plainText, key1, Hex.toHexString(iv), padding);
//        String decrypt3 = decryptHex_CBC(encrypt3, key1, Hex.toHexString(iv), padding);
//        log.debug("\n加密 : {}\n解密 : {}", encrypt3, decrypt3);
    }

}

标签:Java,String,SM4,padding,国密,static,key,return,data
From: https://www.cnblogs.com/zhuzhongxing/p/17656222.html

相关文章

  • LeetCode-21. 合并两个有序链表(Java)
    这是我在51CTO博客开启的写作之路,第一次正式写博客记录我在LeetCode的刷题日,希望能帮助更多的小伙伴攻面自己心仪的公司offer。如下对于 LeetCode-21.合并两个有序链表,进行全面解析并小结解题思路,同学们请参考:1.题目描述将两个升序链表合并为一个新的 升序 链表并返回。新链表......
  • C# 实现 国密SM4/ECB/PKCS7Padding对称加密解密
    C# 实现国密SM4/ECB/PKCS7Padding对称加密解密,为了演示方便本问使用的是VisualStudio2022来构建代码的1、新建项目,之后选择项目鼠标右键选择 管理NuGet程序包管理,输入 BouncyCastle回车添加BouncyCastle程序包 2、代码如下:CBC模式byt......
  • Java设计模式
    装饰器模式:装饰器模式是指在不改变现有对象结构的情况下,动态的给改对象增加一些职责(即增加其额外功能)的模式。装饰器模式通常在以下几种情况使用。当需要给一个现有类添加附加职责,而又不能采用生成子类的方法进行扩充时。例如,该类被隐藏或者该类是终极类或者采用继承方式会产生......
  • Java的三大版本
    Java的三大版本WriteOnce、RunAnywhere一次编译,到处运行JavaSE标准版(桌面程序,控制台开发),这是学习Java的基础,必须牢固掌握。JavaME嵌入式开发(手机,小家电),这个现在基本上没有人再使用,可以忽略,但是要知道有这个版本。JavaEEE企业级开发(web端,服务器开发),学习这个版本之前先要......
  • CentOS7.9搭建开发环境(Java、MySQL、Nginx、Redis)
    系统使用的阿里云CentOS7.964位SCC版。先安装个文件上传下载工具lrzsz,xshell登录终端,运行下面的命令:yuminstall-ylszrz 这是因为yum源的问题,需要修改yum配置。执行以下命令:cd/etc/yum.repos.dmvCentOS-Base.repoCentOS-Base.repo.backupwgethttp://mirrors.......
  • Java的第一课,特性和优势
    Java的特性和优势简单性面向对象可移植性高性能分布式动态性多线程安全性健壮性以上特性和优势会在以后的博客中逐一展示,尽请期待! ......
  • Java流程控制if选择结构
    if选择结构单选择结构:编程中很多时候需要去判断一个东西是否可行,然后我们才去执行,这样一个过程用if语句来表示,语法:if(布尔表达式){//如果条件成立,将执行的语句}例:packageshuct;importjava.util.Scanner;publicclassIfDemo01{publicstaticvoidmain(Str......
  • TypeScript(TS)JavaScript(JS)中的所有循环方法
    for循环:for(leti=0;i<array.length;i++){//循环体}for…of循环:for(constelementofarray){//循环体}forEach方法:array.forEach((element)=>{//循环体});map方法:constnewArray=array.map((element)=>{//对......
  • java基础数据类型-int类型-day02
    目录1.变量的命名2.常量3.变量4.进制4.1进制转换4.2整型数据类型1.变量的命名记住一点:不可以以数字开头类名:首字母大写的驼峰体变量名,方法名:首字母小写的驼峰体包的名字:与python语言一样全部小写2.常量整形:123实数型:3.14字符:‘a’字符串:"abc"布尔值:truefalse......
  • 《深入理解Java虚拟机》读书笔记:方法调用
      方法调用并不等同于方法执行,方法调用阶段唯一的任务就是确定被调用方法的版本(即调用哪一个方法),暂时还不涉及方法内部的具体运行过程。在程序运行时,进行方法调用是最普遍、最频繁的操作,但前面已经讲过,Class文件的编译过程中不包含传统编译中的连接步骤,一切方法调用在Class文件......