All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.dxy.library.util.cipher.symmetry.SM4Utils Maven / Gradle / Ivy

The newest version!
package com.dxy.library.util.cipher.symmetry;

import com.dxy.library.util.cipher.constant.Algorithm;
import com.dxy.library.util.cipher.constant.AlgorithmUtils;
import com.dxy.library.util.cipher.constant.Mode;
import com.dxy.library.util.cipher.constant.Padding;
import com.dxy.library.util.common.StringUtils;
import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.AlgorithmParameters;
import java.security.NoSuchAlgorithmException;
import java.security.Security;

/**
 * SM4工具类
 * 无线局域网标准的分组数据算法,密钥长 128,块长128,类似AES
 * @author duanxinyuan
 * 2019/2/25 15:47
 */
public class SM4Utils {

    static {
        //导入Provider,BouncyCastle是一个开源的加解密解决方案,主页在http://www.bouncycastle.org/
        Security.addProvider(new BouncyCastleProvider());
    }

    /**
     * SM4加密(最常用方式之一,使用SM4/ECB/PKCS5Padding方式,无偏移量)
     * @param data 密文(Base64编码)
     * @param key 密钥(Base64编码),长度必须是16位
     */
    public static String encrypt(String data, String key) {
        return encrypt(data, key, null, Mode.ECB, Padding.PKCS5Padding);
    }

    /**
     * SM4加密(最常用方式之一,使用SM4/ECB/PKCS5Padding方式,无偏移量)
     * @param data 密文
     * @param key 密钥,长度必须是16位
     */
    public static byte[] encrypt(byte[] data, byte[] key) {
        return encrypt(data, key, null, Mode.ECB, Padding.PKCS5Padding);
    }

    /**
     * SM4加密(最常用方式之一,使用SM4/CBC/PKCS7Padding方式)
     * @param data 密文(Base64编码)
     * @param key 密钥(Base64编码),长度必须是16位
     * @param iv 偏移量,长度必须为16位
     */
    public static String encrypt(String data, String key, String iv) {
        return encrypt(data, key, iv, Mode.CBC, Padding.PKCS7Padding);
    }

    /**
     * SM4加密(最常用方式之一,使用SM4/CBC/PKCS7Padding方式)
     * @param data 密文
     * @param key 密钥,长度必须是16位
     * @param iv 偏移量,长度必须为16位
     */
    public static byte[] encrypt(byte[] data, byte[] key, String iv) {
        return encrypt(data, key, iv, Mode.CBC, Padding.PKCS7Padding);
    }

    /**
     * SM4加密(不带偏移量)
     * @param data 密文(Base64编码)
     * @param key 密钥(Base64编码),长度必须是16位
     */
    public static String encrypt(String data, String key, Mode mode, Padding padding) {
        return encrypt(data, key, null, mode, padding);
    }

    /**
     * SM4加密(不带偏移量)
     * @param data 密文
     * @param key 密钥,长度必须是16位
     * @return 密文(Base64编码)
     */
    public static byte[] encrypt(byte[] data, byte[] key, Mode mode, Padding padding) {
        return encrypt(data, key, null, mode, padding);
    }

    /**
     * SM4加密
     * @param data 明文(Base64编码)
     * @param key 密钥(Base64编码),长度必须是16位
     * @param iv 偏移量,长度必须为16位
     * @param mode 密码块工作模式
     * @param padding 填充方式
     * @return 密文(Base64编码)
     */
    public static String encrypt(String data, String key, String iv, Mode mode, Padding padding) {
        if (StringUtils.isEmpty(data)) {
            return null;
        }
        byte[] encrypt = encrypt(data.getBytes(), Base64.decodeBase64(key), iv, mode, padding);
        return Base64.encodeBase64String(encrypt);
    }

    /**
     * SM4加密
     * @param data 明文
     * @param key 密钥,长度必须是16位
     * @param iv 偏移量,长度必须为16位
     * @param mode 密码块工作模式
     * @param padding 填充方式
     * @return 密文
     */
    public static byte[] encrypt(byte[] data, byte[] key, String iv, Mode mode, Padding padding) {
        check(key, iv, mode, padding);
        try {
            SecretKeySpec secretKeySpec = getSecretKeySpec(key);
            String algorithm = AlgorithmUtils.getAlgorithm(Algorithm.SM4, mode, padding);
            // 创建密码器
            Cipher cipher = Cipher.getInstance(algorithm);
            // 初始化
            if (StringUtils.isNotEmpty(iv)) {
                AlgorithmParameters parameters = AlgorithmParameters.getInstance(Algorithm.SM4.getAlgorithm());
                parameters.init(new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8)));
                cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, parameters);
            } else {
                cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
            }
            //加密
            return cipher.doFinal(data);
        } catch (Exception e) {
            throw new RuntimeException("SM4 encrypt error", e);
        }
    }

    /**
     * SM4解密(最常用方式之一,使用SM4/ECB/PKCS5Padding方式,无偏移量)
     * @param data 密文(Base64编码)
     * @param key 密钥(Base64编码),长度必须是16位
     * @return 明文
     */
    public static String decrypt(String data, String key) {
        return decrypt(data, key, null, Mode.ECB, Padding.PKCS5Padding);
    }

    /**
     * SM4解密(最常用方式之一,使用SM4/ECB/PKCS5Padding方式,无偏移量)
     * @param data 密文
     * @param key 密钥,长度必须是16位
     * @return 明文
     */
    public static byte[] decrypt(byte[] data, byte[] key) {
        return decrypt(data, key, null, Mode.ECB, Padding.PKCS5Padding);
    }

    /**
     * SM4解密(最常用方式之一,使用SM4/CBC/PKCS7Padding方式)
     * @param data 密文(Base64编码)
     * @param key 密钥(Base64编码),长度必须是16位
     * @param iv 偏移量,长度必须为16位
     * @return 明文
     */
    public static String decrypt(String data, String key, String iv) {
        return decrypt(data, key, iv, Mode.CBC, Padding.PKCS7Padding);
    }

    /**
     * SM4解密(最常用方式之一,使用SM4/CBC/PKCS7Padding方式)
     * @param data 密文
     * @param key 密钥,长度必须是16位
     * @param iv 偏移量,长度必须为16位
     * @return 明文
     */
    public static byte[] decrypt(byte[] data, byte[] key, String iv) {
        return decrypt(data, key, iv, Mode.CBC, Padding.PKCS7Padding);
    }

    /**
     * SM4解密(不带偏移量)
     * @param data 密文(Base64编码)
     * @param key 密钥(Base64编码),长度必须是16位
     * @return 明文
     */
    public static String decrypt(String data, String key, Mode mode, Padding padding) {
        return decrypt(data, key, null, mode, padding);
    }

    /**
     * SM4解密(不带偏移量)
     * @param data 密文
     * @param key 密钥,长度必须是16位
     * @return 明文
     */
    public static byte[] decrypt(byte[] data, byte[] key, Mode mode, Padding padding) {
        return decrypt(data, key, null, mode, padding);
    }

    /**
     * SM4解密
     * @param data 密文(Base64编码)
     * @param key 密钥(Base64编码),长度必须是16位
     * @param iv 偏移量,长度必须为16位
     * @param mode 密码块工作模式
     * @param padding 填充方式
     * @return 明文
     */
    public static String decrypt(String data, String key, String iv, Mode mode, Padding padding) {
        if (StringUtils.isEmpty(data)) {
            return null;
        }
        byte[] decrypt = decrypt(Base64.decodeBase64(data), Base64.decodeBase64(key), iv, mode, padding);
        return new String(decrypt);
    }

    /**
     * SM4解密
     * @param data 密文
     * @param key 密钥,长度必须是16位
     * @param iv 偏移量,长度必须为16位
     * @param mode 密码块工作模式
     * @param padding 填充方式
     * @return 明文
     */
    public static byte[] decrypt(byte[] data, byte[] key, String iv, Mode mode, Padding padding) {
        check(key, iv, mode, padding);
        try {
            SecretKeySpec secretKeySpec = getSecretKeySpec(key);
            String algorithm = AlgorithmUtils.getAlgorithm(Algorithm.SM4, mode, padding);
            // 创建密码器
            Cipher cipher = Cipher.getInstance(algorithm);
            // 初始化
            if (StringUtils.isNotEmpty(iv)) {
                AlgorithmParameters parameters = AlgorithmParameters.getInstance(Algorithm.SM4.getAlgorithm());
                parameters.init(new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8)));
                cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, parameters);
            } else {
                cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
            }
            //解密
            return cipher.doFinal(data);
        } catch (Exception e) {
            throw new RuntimeException("SM4 decrypt error", e);
        }
    }

    /**
     * 生成SM4的Key(128位,Base64编码)
     * @return 密钥(Base64编码)
     */
    public static String genarateKeyBase64() {
        return Base64.encodeBase64String(genarateKey());
    }

    /**
     * 生成SM4的Key,密钥长度为 128
     * @return 密钥
     */
    public static byte[] genarateKey() {
        try {
            KeyGenerator keyGenerator = KeyGenerator.getInstance(Algorithm.SM4.getAlgorithm());
            keyGenerator.init(128);
            SecretKey secretKey = keyGenerator.generateKey();
            return secretKey.getEncoded();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("SM4 key genarate error", e);
        }
    }

    private static SecretKeySpec getSecretKeySpec(byte[] key) {
        return new SecretKeySpec(key, Algorithm.SM4.getAlgorithm());
    }

    private static void check(byte[] key, String iv, Mode mode, Padding padding) {
        checkKey(key);
        checkModeAndPadding(mode, padding);
        if (StringUtils.isNotEmpty(iv)) {
            checkIv(iv);
            if (mode == Mode.ECB) {
                throw new RuntimeException("SM4 ECB mode does not use an IV");
            }
        }
    }

    /**
     * 校验SM4密码块工作模式和填充模式
     */
    private static void checkModeAndPadding(Mode mode, Padding padding) {
        if (mode == Mode.NONE) {
            throw new RuntimeException("invalid SM4 mode");
        }
        if (padding == Padding.SSL3Padding || padding == Padding.PKCS1Padding) {
            throw new RuntimeException("invalid SM4 padding");
        }
        if (padding == Padding.NoPadding) {
            if (mode == Mode.ECB || mode == Mode.CBC) {
                throw new RuntimeException("invalid SM4 algorithm");
            }
        }
    }

    /**
     * 校验SM4密钥,长度必须是16位
     */
    private static void checkKey(byte[] key) {
        if (key == null) {
            throw new RuntimeException("SM4 key cannot be null");
        }
        if (key.length != 16) {
            throw new RuntimeException("SM4 key not 16 bytes long");
        }
    }

    /**
     * 校验SM4偏移量,长度必须是16位
     */
    private static void checkIv(String iv) {
        if (iv.length() != 16) {
            throw new RuntimeException("SM4 iv not 16 bytes long");
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy