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:截图展示加解密后的效果。