Android 数据SM4传输加解密

第一:场景介绍

  甲方要求,数据在传输过程中要加密。

第二:选择加解密方式:

这里面Android 和服务端都是选择SM4

第三:实现方式

1:服务端采用SpringBoot,在pom.xml 文件下添加依赖

		<dependency>
			<groupId>org.bouncycastle</groupId>
			<artifactId>bcprov-jdk15on</artifactId>
			<version>1.64</version>
		</dependency>

2:在Android 端同样添加依赖,在module 下面build.gradle

  implementation group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: '1.64'

3:服务端SM4工具类

package com.wansun.datahouse.visit.utils;

import org.apache.tomcat.util.buf.HexUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.Security;

/**
 * TODO:
 * 国密SM4算法
 *
 * @author Blade
 * @date 2022/5/9 17:13
 */
public class Sm4Util {

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

    /**
     * SM4算法ECB模式PKCS5Padding填充方式
     */
    public static final String ALGORITHM_NAME_ECB_PADDING = "SM4/ECB/PKCS5Padding";

    /**
     * SM4算法ECB模式不填充
     */
    public static final String ALGORITHM_NAME_ECB_NOPADDING = "SM4/ECB/NoPadding";

    /**
     * SM4算法CBC模式PKCS5Padding填充方式
     */
    public static final String ALGORITHM_NAME_CBC_PADDING = "SM4/CBC/PKCS5Padding";

    /**
     * SM4算法CBC模式不填充
     */
    public static final String ALGORITHM_NAME_CBC_NOPADDING = "SM4/CBC/NoPadding";

    /**
     * 默认key长度
     */
    public static final int DEFAULT_KEY_SIZE = 128;

    static {
        // 加入 BC 实现
        Security.addProvider(new BouncyCastleProvider());
    }

    /**
     * 生成秘钥 key
     *
     * @return key
     * @throws NoSuchAlgorithmException 异常
     * @throws NoSuchProviderException  异常
     */
    private static byte[] generateKey() throws NoSuchAlgorithmException, NoSuchProviderException {
        return generateKey(DEFAULT_KEY_SIZE);
    }

    /**
     * 生成秘钥 key
     *
     * @param keySize 秘钥长度
     * @return key
     * @throws NoSuchAlgorithmException 异常
     * @throws NoSuchProviderException  异常
     */
    private static byte[] generateKey(int keySize) throws NoSuchAlgorithmException, NoSuchProviderException {
        KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM_NAME, BouncyCastleProvider.PROVIDER_NAME);
        kg.init(keySize, new SecureRandom());
        return kg.generateKey().getEncoded();
    }

    /**
     * 生成ECB模式的cipher
     *
     * @param algorithmName 算法名称
     * @param mode          模式
     * @param key           秘钥
     * @return {@link Cipher} 密文
     * @throws NoSuchAlgorithmException 异常
     * @throws NoSuchProviderException  异常
     * @throws NoSuchPaddingException   异常
     * @throws InvalidKeyException      异常
     */
    private static Cipher generateEcbCipher(String algorithmName, int mode, byte[] key)
            throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException,
            InvalidKeyException {
        Cipher cipher = Cipher.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME);
        Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME);
        cipher.init(mode, sm4Key);
        return cipher;
    }

    /**
     * 用ECB模式和PKCS5Padding填充的方式 进行加密
     *
     * @param key  秘钥
     * @param data 加密前的数据
     * @return 加密后的数据
     * @throws InvalidKeyException       异常
     * @throws NoSuchAlgorithmException  异常
     * @throws NoSuchProviderException   异常
     * @throws NoSuchPaddingException    异常
     * @throws IllegalBlockSizeException 异常
     * @throws BadPaddingException       异常
     */
    private static byte[] encryptEcbPadding(byte[] key, byte[] data)
            throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException,
            NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
        Cipher cipher = generateEcbCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.ENCRYPT_MODE, key);
        return cipher.doFinal(data);
    }

    /**
     * 用ECB模式和PKCS5Padding填充的方式 进行解密
     *
     * @param key        秘钥
     * @param cipherText 加密后的数据
     * @return 解密后的数据
     * @throws InvalidKeyException       异常
     * @throws NoSuchAlgorithmException  异常
     * @throws NoSuchProviderException   异常
     * @throws NoSuchPaddingException    异常
     * @throws IllegalBlockSizeException 异常
     * @throws BadPaddingException       异常
     */
    private static byte[] decryptEcbPadding(byte[] key, byte[] cipherText)
            throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException,
            NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException {
        Cipher cipher = generateEcbCipher(ALGORITHM_NAME_ECB_PADDING, Cipher.DECRYPT_MODE, key);
        return cipher.doFinal(cipherText);
    }


    /**
     * 用ECB模式和不填充的方式 进行加密
     *
     * @param key  秘钥
     * @param data 加密前的数据
     * @return 加密后的数据
     * @throws InvalidKeyException       异常
     * @throws NoSuchAlgorithmException  异常
     * @throws NoSuchProviderException   异常
     * @throws NoSuchPaddingException    异常
     * @throws IllegalBlockSizeException 异常
     * @throws BadPaddingException       异常
     */
    private static byte[] encryptEcbNoPadding(byte[] key, byte[] data)
            throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException,
            NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
        Cipher cipher = generateEcbCipher(ALGORITHM_NAME_ECB_NOPADDING, Cipher.ENCRYPT_MODE, key);
        return cipher.doFinal(data);
    }

    /**
     * 用ECB模式和不填充的方式 进行解密
     *
     * @param key        秘钥
     * @param cipherText 解密前的数据
     * @return 解密后的数据
     * @throws InvalidKeyException       异常
     * @throws NoSuchAlgorithmException  异常
     * @throws NoSuchProviderException   异常
     * @throws NoSuchPaddingException    异常
     * @throws IllegalBlockSizeException 异常
     * @throws BadPaddingException       异常
     */
    private static byte[] decryptEcbNoPadding(byte[] key, byte[] cipherText)
            throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException,
            NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException {
        Cipher cipher = generateEcbCipher(ALGORITHM_NAME_ECB_NOPADDING, Cipher.DECRYPT_MODE, key);
        return cipher.doFinal(cipherText);
    }

    /**
     * 生成CBC模式的cipher
     *
     * @param algorithmName 算法名称
     * @param mode          模式
     * @param key           秘钥
     * @param iv            偏移量(自定义的字符串)
     * @return {@link Cipher}
     * @throws NoSuchAlgorithmException 异常
     * @throws NoSuchProviderException  异常
     * @throws NoSuchPaddingException   异常
     * @throws InvalidKeyException      异常
     */
    private static Cipher generateCbcCipher(String algorithmName, int mode, byte[] key, byte[] iv)
            throws InvalidKeyException, InvalidAlgorithmParameterException, NoSuchAlgorithmException,
            NoSuchProviderException, NoSuchPaddingException {
        Cipher cipher = Cipher.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME);
        Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME);
        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
        cipher.init(mode, sm4Key, ivParameterSpec);
        return cipher;
    }

    /**
     * 用CBC模式和PKCS5Padding填充的方式 进行加密
     *
     * @param key  秘钥
     * @param iv   偏移量(自定义的字符串)
     * @param data 加密前的数据
     * @return 加密后的数据
     * @throws InvalidKeyException       异常
     * @throws NoSuchAlgorithmException  异常
     * @throws NoSuchProviderException   异常
     * @throws NoSuchPaddingException    异常
     * @throws IllegalBlockSizeException 异常
     * @throws BadPaddingException       异常
     */
    private static byte[] encryptCbcPadding(byte[] key, byte[] iv, byte[] data)
            throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException,
            NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException,
            InvalidAlgorithmParameterException {
        Cipher cipher = generateCbcCipher(ALGORITHM_NAME_CBC_PADDING, Cipher.ENCRYPT_MODE, key, iv);
        return cipher.doFinal(data);
    }

    /**
     * 用CBC模式和PKCS5Padding填充的方式 进行解密
     *
     * @param key        秘钥
     * @param iv         偏移量(自定义的字符串)
     * @param cipherText 解密前的数据
     * @return 解密后的数据
     * @throws InvalidKeyException       异常
     * @throws NoSuchAlgorithmException  异常
     * @throws NoSuchProviderException   异常
     * @throws NoSuchPaddingException    异常
     * @throws IllegalBlockSizeException 异常
     * @throws BadPaddingException       异常
     */
    private static byte[] decryptCbcPadding(byte[] key, byte[] iv, byte[] cipherText)
            throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException,
            NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException,
            InvalidAlgorithmParameterException {
        Cipher cipher = generateCbcCipher(ALGORITHM_NAME_CBC_PADDING, Cipher.DECRYPT_MODE, key, iv);
        return cipher.doFinal(cipherText);
    }

    /**
     * 用CBC模式和不填充的方式 进行加密
     *
     * @param key  秘钥
     * @param iv   偏移量(自定义的字符串)
     * @param data 加密前的数据
     * @return 加密后的数据
     * @throws InvalidKeyException       异常
     * @throws NoSuchAlgorithmException  异常
     * @throws NoSuchProviderException   异常
     * @throws NoSuchPaddingException    异常
     * @throws IllegalBlockSizeException 异常
     * @throws BadPaddingException       异常
     */
    private static byte[] encryptCbcNoPadding(byte[] key, byte[] iv, byte[] data)
            throws InvalidKeyException, NoSuchAlgorithmException, NoSuchProviderException,
            NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException,
            InvalidAlgorithmParameterException {
        Cipher cipher = generateCbcCipher(ALGORITHM_NAME_CBC_NOPADDING, Cipher.ENCRYPT_MODE, key, iv);
        return cipher.doFinal(data);
    }

    /**
     * 用ECB模式和不填充的方式 进行解密
     *
     * @param key        秘钥
     * @param iv         偏移量(自定义的字符串)
     * @param cipherText 加密后的数据
     * @return 解密后的数据
     * @throws InvalidKeyException       异常
     * @throws NoSuchAlgorithmException  异常
     * @throws NoSuchProviderException   异常
     * @throws NoSuchPaddingException    异常
     * @throws IllegalBlockSizeException 异常
     * @throws BadPaddingException       异常
     */
    private static byte[] decryptCbcNoPadding(byte[] key, byte[] iv, byte[] cipherText)
            throws IllegalBlockSizeException, BadPaddingException, InvalidKeyException,
            NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException,
            InvalidAlgorithmParameterException {
        Cipher cipher = generateCbcCipher(ALGORITHM_NAME_CBC_NOPADDING, Cipher.DECRYPT_MODE, key, iv);
        return cipher.doFinal(cipherText);
    }

    /**
     * 生成key并且转换为16进制字符串
     *
     * @return key
     * @throws NoSuchProviderException  异常
     * @throws NoSuchAlgorithmException 异常
     */
    public static String generateKey_Hex() throws Exception {
        return HexUtils.toHexString(generateKey());
    }

    /**
     * 用CBC模式和PKCS5Padding填充的方式 进行加密
     *
     * @param key  秘钥
     * @param iv   偏移量(自定义的字符串)
     * @param data 加密数据
     * @return 加密后的转换成Hex的数据
     */
    public static String encryptCbcPaddingHex(String key, String iv, String data) throws Exception {

        String iv_hex = HexUtils.toHexString(iv.getBytes());

        return HexUtils.toHexString(encryptCbcPadding(HexUtils.fromHexString(key),
                HexUtils.fromHexString(iv_hex), data.getBytes()));
    }

    /**
     * 用CBC模式和PKCS5Padding填充的方式 进行解密
     *
     * @param key        秘钥
     * @param iv         偏移量(自定义的字符串)
     * @param cipherText 待解密数据
     * @return 解密后的数据
     */
    public static String decryptCbcPaddingHex(String key, String iv, String cipherText) throws Exception {
        return new String(decryptCbcPadding(HexUtils.fromHexString(key), iv.getBytes(),
                HexUtils.fromHexString(cipherText)));
    }

    public static void main(String[] args) throws Exception {
        String data = "{"test":"湖北利川深圳"}";
        String iv = "blade12345678910";
//        String hexKey = generateKey_Hex();
        String hexKey = "20785fae41a54c65ab95d2db1abc7f9f";
        System.out.println("hexKey===" + hexKey);
        String cipher = encryptCbcPaddingHex(hexKey, iv, data);
        System.out.println("cipher==" + cipher);
        String b = decryptCbcPaddingHex(hexKey, iv, cipher);
        System.out.println(b);
    }
}

4:Android SM4 工具类,要把 

BouncyCastleProvider.PROVIDER_NAME 贴换为 new BouncyCastleProvider()  不然报错

5:

分别在拦截器里面调用 加解密方法。hexKey 可以自己生成。iv 自己定义16位

    String hexKey = "20785fae41a54c65ab95d2db1abc7f9f";
    String iv = "blade12345678910";

加密方法:

String encryptJson = Sm4Util.encryptCbcPaddingHex(hexKey, iv, String.valueOf(json));

解密方法:

 String decryptJson = Sm4Util.decryptCbcPaddingHex(hexKey, iv, String.valueOf(data));

6:这里面就不展示拦截器里面的代码。

7:截图展示加解密后的效果。