发布于2021-02-27 20:09 阅读(1377) 评论(0) 点赞(2) 收藏(0)
工具类最开始是参考这篇博客java sm4国密算法加密、解密,但是该篇博客使用的是EBC模式,所以参考其他文章改成了CBC模式。由于本人并不专于算法和密码学,所以如果发现工具类存在问题或者可优化地方,欢迎评论处提出。
<!-- https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.54</version>
</dependency>
/**
* 自动生成密钥
*
* @return
* @explain
*/
public static byte[] generateKey() throws Exception {
return generateKey(DEFAULT_KEY_SIZE);
}
public static String generateKeyString() throws Exception {
return ByteUtils.toHexString(generateKey());
}
/**
* @param keySize
* @return
* @throws Exception
* @explain
*/
public static byte[] generateKey(int keySize) throws Exception {
KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM_NAME, BouncyCastleProvider.PROVIDER_NAME);
kg.init(keySize, new SecureRandom());
return kg.generateKey().getEncoded();
}
/**
* sm4加密
*
* @param hexKey 16进制密钥(忽略大小写)
* @param paramStr 待加密字符串
* @return 返回16进制的加密字符串
* @throws Exception
* @explain 加密模式:CBC
*/
public static String protectMsg(String hexKey, String paramStr) throws Exception {
String result = "";
// 16进制字符串-->byte[]
byte[] keyData = ByteUtils.fromHexString(hexKey);
// String-->byte[]
byte[] srcData = paramStr.getBytes(ENCODING);
// 加密后的数组
byte[] cipherArray = encrypt_Cbc_Padding(keyData, srcData);
// byte[]-->hexString
result = ByteUtils.toHexString(cipherArray);
return result;
}
/**
* 加密模式之CBC
*
* @param key
* @param data
* @return
* @throws Exception
* @explain
*/
public static byte[] encrypt_Cbc_Padding(byte[] key, byte[] data) throws Exception {
Cipher cipher = generateCbcCipher(ALGORITHM_NAME_CBC_PADDING, Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(data);
}
/**
* sm4解密
*
* @param hexKey 16进制密钥
* @param text 16进制的加密字符串(忽略大小写)
* @return 解密后的字符串
* @throws Exception
* @explain 解密模式:采用CBC
*/
public static String uncoverMsg(String hexKey, String text) throws Exception {
// 用于接收解密后的字符串
String result = "";
// hexString-->byte[]
byte[] keyData = ByteUtils.fromHexString(hexKey);
// hexString-->byte[]
byte[] resultData = ByteUtils.fromHexString(text);
// 解密
byte[] srcData = decrypt_Cbc_Padding(keyData, resultData);
// byte[]-->String
result = new String(srcData, ENCODING);
return result;
}
/**
* 解密
*
* @param key
* @param cipherText
* @return
* @throws Exception
* @explain
*/
public static byte[] decrypt_Cbc_Padding(byte[] key, byte[] cipherText) throws Exception {
Cipher cipher = generateCbcCipher(ALGORITHM_NAME_CBC_PADDING, Cipher.DECRYPT_MODE, key);
return cipher.doFinal(cipherText);
}
package com.example.pass.common.utils;
import com.example.pass.common.enums.BaseSmEnum;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.AlgorithmParameters;
import java.security.Key;
import java.security.SecureRandom;
import java.security.Security;
import java.util.Arrays;
/**
* @description:
* @author: dsy
* @date: 2021/2/25 10:56
*/
public class Sm4Util {
static {
Security.addProvider(new BouncyCastleProvider());
}
private static final String ENCODING = BaseSmEnum.ENCODING.getMsg();
public static final String ALGORITHM_NAME = BaseSmEnum.ALGORITHM_NAME.getMsg();
// 加密算法/分组加密模式/分组填充方式
// PKCS5Padding-以8个字节为一组进行分组加密
// 定义分组加密模式使用:PKCS5Padding
public static final String ALGORITHM_NAME_CBC_PADDING = BaseSmEnum.ALGORITHM_NAME_CBC_PADDING.getMsg();
// 128-32位16进制;256-64位16进制
public static final int DEFAULT_KEY_SIZE = 128;
/**
* 自动生成密钥
*
* @return
* @explain
*/
public static byte[] generateKey() throws Exception {
return generateKey(DEFAULT_KEY_SIZE);
}
public static String generateKeyString() throws Exception {
return ByteUtils.toHexString(generateKey());
}
/**
* @param keySize
* @return
* @throws Exception
* @explain
*/
public static byte[] generateKey(int keySize) throws Exception {
KeyGenerator kg = KeyGenerator.getInstance(ALGORITHM_NAME, BouncyCastleProvider.PROVIDER_NAME);
kg.init(keySize, new SecureRandom());
return kg.generateKey().getEncoded();
}
/**
* sm4加密
*
* @param hexKey 16进制密钥(忽略大小写)
* @param paramStr 待加密字符串
* @return 返回16进制的加密字符串
* @throws Exception
* @explain 加密模式:CBC
*/
public static String protectMsg(String hexKey, String paramStr) throws Exception {
String result = "";
// 16进制字符串-->byte[]
byte[] keyData = ByteUtils.fromHexString(hexKey);
// String-->byte[]
byte[] srcData = paramStr.getBytes(ENCODING);
// 加密后的数组
byte[] cipherArray = encrypt_Cbc_Padding(keyData, srcData);
// byte[]-->hexString
result = ByteUtils.toHexString(cipherArray);
return result;
}
/**
* 加密模式之CBC
*
* @param key
* @param data
* @return
* @throws Exception
* @explain
*/
public static byte[] encrypt_Cbc_Padding(byte[] key, byte[] data) throws Exception {
Cipher cipher = generateCbcCipher(ALGORITHM_NAME_CBC_PADDING, Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(data);
}
private static Cipher generateCbcCipher(String algorithmName, int mode, byte[] key) throws Exception {
Cipher cipher = Cipher.getInstance(algorithmName, BouncyCastleProvider.PROVIDER_NAME);
Key sm4Key = new SecretKeySpec(key, ALGORITHM_NAME);
cipher.init(mode, sm4Key, generateIV());
return cipher;
}
//生成iv
public static AlgorithmParameters generateIV() throws Exception {
//iv 为一个 16 字节的数组,这里采用和 iOS 端一样的构造方法,数据全为0
byte[] iv = new byte[16];
Arrays.fill(iv, (byte) 0x00);
AlgorithmParameters params = AlgorithmParameters.getInstance(ALGORITHM_NAME);
params.init(new IvParameterSpec(iv));
return params;
}
/**
* sm4解密
*
* @param hexKey 16进制密钥
* @param text 16进制的加密字符串(忽略大小写)
* @return 解密后的字符串
* @throws Exception
* @explain 解密模式:采用CBC
*/
public static String uncoverMsg(String hexKey, String text) throws Exception {
// 用于接收解密后的字符串
String result = "";
// hexString-->byte[]
byte[] keyData = ByteUtils.fromHexString(hexKey);
// hexString-->byte[]
byte[] resultData = ByteUtils.fromHexString(text);
// 解密
byte[] srcData = decrypt_Cbc_Padding(keyData, resultData);
// byte[]-->String
result = new String(srcData, ENCODING);
return result;
}
/**
* 解密
*
* @param key
* @param cipherText
* @return
* @throws Exception
* @explain
*/
public static byte[] decrypt_Cbc_Padding(byte[] key, byte[] cipherText) throws Exception {
Cipher cipher = generateCbcCipher(ALGORITHM_NAME_CBC_PADDING, Cipher.DECRYPT_MODE, key);
return cipher.doFinal(cipherText);
}
}
package com.example.pass.common.enums;
public enum BaseSmEnum {
/**
*
*/
ENCODING("UTF-8"),
ALGORITHM_NAME("SM4"),
ALGORITHM_NAME_CBC_PADDING("SM4/CBC/PKCS5Padding"),
;
private String msg;
BaseSmEnum(String msg) {
this.msg = msg;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
@Override
public String toString() {
return "BaseEnum{" +
"msg='" + msg + '\'' +
'}';
}
}
在对安全要求较高的项目中,可以将密钥分开存储,比如密钥分开为两半,以字节形式存储,防止内存中存在完整密钥,使用后重置各变量,比如下图:
原文链接:https://blog.csdn.net/not_say/article/details/114080841
作者:java王侯
链接:http://www.javaheidong.com/blog/article/103977/359ce815691d5d21a56b/
来源:java黑洞网
任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任
昵称:
评论内容:(最多支持255个字符)
---无人问津也好,技不如人也罢,你都要试着安静下来,去做自己该做的事,而不是让内心的烦躁、焦虑,坏掉你本来就不多的热情和定力
Copyright © 2018-2021 java黑洞网 All Rights Reserved 版权所有,并保留所有权利。京ICP备18063182号-2
投诉与举报,广告合作请联系vgs_info@163.com或QQ3083709327
免责声明:网站文章均由用户上传,仅供读者学习交流使用,禁止用做商业用途。若文章涉及色情,反动,侵权等违法信息,请向我们举报,一经核实我们会立即删除!