com.siashan.toolkit.crypt.symmetric.SymmetricCrypt Maven / Gradle / Ivy
package com.siashan.toolkit.crypt.symmetric;
import com.siashan.toolkit.crypt.CryptException;
import com.siashan.toolkit.crypt.DecoderException;
import com.siashan.toolkit.crypt.KeyUtil;
import com.siashan.toolkit.crypt.SecureUtil;
import com.siashan.toolkit.crypt.binary.Base64;
import com.siashan.toolkit.crypt.binary.Hex;
import com.siashan.toolkit.crypt.util.StringUtils;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import java.io.Serializable;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
/**
* 对称加密算法
* 在对称加密算法中,数据发信方将明文(原始数据)和加密密钥一起经过特殊加密算法处理后,使其变成复杂的加密密文发送出去。
* 收信方收到密文后,若想解读原文,则需要使用加密用过的密钥及相同算法的逆算法对密文进行解密,才能使其恢复成可读明文。
* 在对称加密算法中,使用的密钥只有一个,发收信双方都使用这个密钥对数据进行加密和解密,这就要求解密方事先必须知道加密密钥。
*
* @author siashan
* @since 1.0.7
*/
public abstract class SymmetricCrypt implements Serializable {
private static final long serialVersionUID = 1L;
private static final Charset UTF_8 = StandardCharsets.UTF_8;
/**
* Cipher负责完成加密或解密工作
*/
private Cipher cipher;
/**
* 是否0填充
*/
private boolean zeroPadding;
public SymmetricCrypt() {
}
/**
* 构建加密对象
*
* @param algorithm 算法 {@link SymmetricAlgorithm }
* @param mode 加密模式
* @param padding 填充模式
* @return 加密对象
*/
public SymmetricCrypt(SymmetricAlgorithm algorithm, Mode mode, Padding padding) {
String fmtAlgorithm = SecureUtil.fmtAlgorithm(algorithm, mode, padding);
if (fmtAlgorithm.contains(Padding.ZeroPadding.name())) {
fmtAlgorithm = fmtAlgorithm.replace(Padding.ZeroPadding.name(), Padding.NoPadding.name());
this.zeroPadding = true;
}
this.cipher = SecureUtil.createCipher(fmtAlgorithm);
}
public abstract SymmetricAlgorithm getAlgorithm();
// -------------------------------------------------- Encrypt ----------------------------------------------------//
/**
* 加密
*
* @param data 被加密的bytes
* @param key 秘钥,支持三种密钥长度:128、192、256位
* @param iv 加密盐/加密向量
* @return 加密后的bytes
*/
private byte[] encrypt(byte[] data, SecretKey key, IvParameterSpec iv) {
try {
if (null == iv) {
cipher.init(Cipher.ENCRYPT_MODE, key);
} else {
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
}
return cipher.doFinal(paddingDataWithZero(data, cipher.getBlockSize(), zeroPadding));
} catch (Exception e) {
throw new CryptException(e);
}
}
/**
* 加密
*
* @param data 被加密的bytes
* @param key 秘钥,支持三种密钥长度:128、192、256位
* @return 加密后的bytes
*/
public byte[] encrypt(byte[] data, byte[] key) {
return encrypt(data, KeyUtil.genKey(getAlgorithm().name(), key), null);
}
/**
* 加密
*
* @param data 数据
* @param key 秘钥
* @return 加密后的Hex
*/
public String encryptHex(byte[] data, byte[] key) {
return Hex.encodeHexString(encrypt(data, key));
}
/**
* 加密
*
* @param data 数据
* @param key 秘钥
* @return 加密后的Base64
*/
public String encryptBase64(byte[] data, byte[] key) {
return Base64.encodeBase64String(encrypt(data, key));
}
/**
* 加密
*
* @param data 被加密的bytes
* @param key 秘钥,支持三种密钥长度:128、192、256位
* @return 加密后的bytes
*/
public byte[] encrypt(byte[] data, String key) {
return encrypt(data, key.getBytes());
}
/**
* 加密
*
* @param data 被加密的bytes
* @param key 秘钥,支持三种密钥长度:128、192、256位
* @return 加密后的Hex
*/
public String encryptHex(byte[] data, String key) {
return Hex.encodeHexString(encrypt(data, key));
}
/**
* 加密
*
* @param data 数据
* @param key 秘钥
* @return 加密后的Base64
*/
public String encryptBase64(byte[] data, String key) {
return Base64.encodeBase64String(encrypt(data, key));
}
/**
* 加密
*
* @param data 被加密的字符串
* @param key 秘钥,支持三种密钥长度:128、192、256位
* @return 加密后的bytes
*/
public byte[] encrypt(String data, byte[] key) {
return encrypt(data.getBytes(UTF_8), key);
}
/**
* 加密
*
* @param data 被加密的字符串
* @param key 秘钥,支持三种密钥长度:128、192、256位
* @return 加密后的Hex
*/
public String encryptHex(String data, byte[] key) {
return Hex.encodeHexString(encrypt(data, key));
}
/**
* 加密
*
* @param data 被加密的字符串
* @param key 秘钥,支持三种密钥长度:128、192、256位
* @return 加密后的Hex
*/
public String encryptBase64(String data, byte[] key) {
return Base64.encodeBase64String(encrypt(data, key));
}
/**
* 加密
*
* @param data 被加密的字符串
* @param key 秘钥,支持三种密钥长度:128、192、256位
* @return 加密后的bytes
*/
public byte[] encrypt(String data, String key) {
return encrypt(data.getBytes(UTF_8), key.getBytes(UTF_8));
}
/**
* 加密
*
* @param data 被加密的字符串
* @param key 秘钥,支持三种密钥长度:128、192、256位
* @return 加密后的Hex
*/
public String encryptHex(String data, String key) {
return Hex.encodeHexString(encrypt(data, key));
}
/**
* 加密
*
* @param data 被加密的字符串
* @param key 秘钥,支持三种密钥长度:128、192、256位
* @return 加密后的Hex
*/
public String encryptBase64(String data, String key) {
return Base64.encodeBase64String(encrypt(data, key));
}
/**
* 加密
*
* @param data 被加密的bytes
* @param key 秘钥,支持三种密钥长度:128、192、256位
* @param iv 加密盐/加密向量
* @return 加密后的bytes
*/
public byte[] encrypt(byte[] data, byte[] key, byte[] iv) {
return encrypt(data, KeyUtil.genKey(cipher.getAlgorithm(), key), new IvParameterSpec(iv));
}
/**
* 加密
*
* @param data 被加密的bytes
* @param key 秘钥,支持三种密钥长度:128、192、256位
* @param iv 加密盐/加密向量
* @return 加密后的Hex
*/
public String encryptHex(byte[] data, byte[] key, byte[] iv) {
return Hex.encodeHexString(encrypt(data, key, iv));
}
/**
* 加密
*
* @param data 被加密的bytes
* @param key 秘钥,支持三种密钥长度:128、192、256位
* @param iv 加密盐/加密向量
* @return 加密后的Base64字符串
*/
public String encryptBase64(byte[] data, byte[] key, byte[] iv) {
return Base64.encodeBase64String(encrypt(data, key, iv));
}
/**
* 加密
*
* @param data 被加密的bytes
* @param key 秘钥,支持三种密钥长度:128、192、256位
* @param iv 加密盐/加密向量
* @return 加密后的bytes
*/
public byte[] encrypt(String data, String key, String iv) {
return encrypt(data.getBytes(UTF_8), key.getBytes(UTF_8), iv.getBytes(UTF_8));
}
/**
* 加密
*
* @param data 被加密的明文字符串
* @param key 秘钥,支持三种密钥长度:128、192、256位
* @param iv 加密盐/加密向量
* @return 加密后的Hex
*/
public String encryptHex(String data, String key, String iv) {
return Hex.encodeHexString(encrypt(data, key, iv));
}
/**
* 加密
*
* @param data 被加密的明文字符串
* @param key 秘钥,支持三种密钥长度:128、192、256位
* @param iv 加密盐/加密向量
* @return 加密后的Base64字符串
*/
public String encryptBase64(String data, String key, String iv) {
return Base64.encodeBase64String(encrypt(data, key, iv));
}
// -------------------------------------------- Decrypt ----------------------------------------------------------//
/**
* 解密
*
* @param data 被加密的bytes
* @param key 秘钥,支持三种密钥长度:128、192、256位
* @param iv 加密盐/加密向量
* @return 解密后的bytes
*/
private byte[] decrypt(byte[] data, SecretKey key, IvParameterSpec iv) {
final int blockSize;
final byte[] decryptData;
try {
if (null == iv) {
cipher.init(Cipher.DECRYPT_MODE, key);
} else {
cipher.init(Cipher.DECRYPT_MODE, key, iv);
}
blockSize = cipher.getBlockSize();
decryptData = cipher.doFinal(data);
} catch (Exception e) {
throw new CryptException(e);
}
return removePadding(decryptData, blockSize);
}
/**
* 解密
*
* @param data 被加密的bytes
* @param key 秘钥,支持三种密钥长度:128、192、256位
* @return 解密后的bytes
*/
public byte[] decrypt(byte[] data, byte[] key) {
return decrypt(data, KeyUtil.genKey(getAlgorithm().name(), key), null);
}
/**
* 解密为字符串
*
* @param bytes 被解密的bytes
* @param key 秘钥
* @return 解密后的String
*/
public String decryptStr(byte[] bytes, byte[] key) {
return StringUtils.newStringUtf8(decrypt(bytes, key));
}
/**
* 解密
*
* @param data 被加密的bytes
* @param key 秘钥,支持三种密钥长度:128、192、256位
* @param iv 加密向量
* @return 解密后的bytes
*/
public byte[] decrypt(byte[] data, byte[] key, byte[] iv) {
return decrypt(data, KeyUtil.genKey(getAlgorithm().name(), key), new IvParameterSpec(iv));
}
/**
* 解密
*
* @param data 被加密的bytes
* @param key 秘钥,支持三种密钥长度:128、192、256位
* @param iv 加密向量
* @return 解密后的bytes
*/
public String decryptToStr(byte[] data, byte[] key, byte[] iv) {
return StringUtils.newStringUtf8(decrypt(data, key, iv));
}
/**
* 解密Hex(16进制)表示的字符串
*
* @param data 被解密的String,必须为16进制字符串
* @return 解密后的bytes
*/
public byte[] decryptHex(String data, byte[] key) throws DecoderException {
return decrypt(Hex.decodeHex(data), key);
}
/**
* 解密Hex(16进制)表示的字符串
*
* @param data 被解密的String,必须为16进制字符串
* @param key 秘钥
* @param iv 加密向量
* @return 解密后的bytes
*/
public byte[] decryptHex(String data, byte[] key, byte[] iv) throws DecoderException {
return decrypt(Hex.decodeHex(data), key, iv);
}
/**
* 解密Hex(16进制)表示的字符串
*
* @param data 被解密的String,必须为16进制字符串
* @param key 秘钥
* @return 解密后的字符串
*/
public String decryptHexToStr(String data, byte[] key) throws DecoderException {
return StringUtils.newStringUtf8(decrypt(Hex.decodeHex(data), key));
}
/**
* 解密Hex(16进制)表示的字符串
*
* @param data 被解密的String,必须为16进制字符串
* @param key 秘钥
* @param iv 加密向量
* @return 解密后的字符串
*/
public String decryptHexToStr(String data, byte[] key, byte[] iv) throws DecoderException {
return StringUtils.newStringUtf8(decrypt(Hex.decodeHex(data), key, iv));
}
/**
* 解密Base64表示的字符串
*
* @param data 被解密的String,必须Base64字符串
* @return 解密后的bytes
*/
public byte[] decryptBase64(String data, byte[] key) {
return decrypt(Base64.decodeBase64(data), key);
}
/**
* 解密Base64表示的字符串
*
* @param data 被解密的String,必须Base64字符串
* @param key 秘钥
* @param iv 加密向量
* @return 解密后的bytes
*/
public byte[] decryptBase64(String data, byte[] key, byte[] iv) {
return decrypt(Base64.decodeBase64(data), key, iv);
}
/**
* 解密Base64表示的字符串
*
* @param data 被解密的String,必须Base64字符串
* @return 解密后的字符串
*/
public String decryptBase64ToStr(String data, byte[] key) {
return StringUtils.newStringUtf8(decrypt(Base64.decodeBase64(data), key));
}
/**
* 解密Base64表示的字符串
*
* @param data 被解密的String,必须Base64字符串
* @param key 秘钥
* @param iv 加密向量
* @return 解密后的字符串
*/
public String decryptBase64ToStr(String data, byte[] key, byte[] iv) {
return StringUtils.newStringUtf8(decrypt(Base64.decodeBase64(data), key, iv));
}
// --------------------------------------------------------------------------------- Private method start
/**
* 数据按照blockSize的整数倍长度填充填充0
*
*
* 在{@link Padding#ZeroPadding} 模式下,且数据长度不是blockSize的整数倍才有效,否则返回原数据
*
*
* 见:https://blog.csdn.net/OrangeJack/article/details/82913804
*
* @param data 数据
* @param blockSize 块大小
* @param isZeroPadding 是否0填充
* @return 填充后的数据,如果isZeroPadding为false或长度刚好,返回原数据
*/
private byte[] paddingDataWithZero(byte[] data, int blockSize, boolean isZeroPadding) {
if (isZeroPadding) {
final int length = data.length;
// 按照块拆分后的数据中多余的数据
final int remainLength = length % blockSize;
if (remainLength > 0) {
// 新长度为blockSize的整数倍,多余部分填充0
return SecureUtil.resize(data, length + blockSize - remainLength);
}
}
return data;
}
/**
* 数据按照blockSize去除填充部分,用于解密
*
*
* 在{@link Padding#ZeroPadding} 模式下,且数据长度不是blockSize的整数倍才有效,否则返回原数据
*
* @param data 数据
* @param blockSize 块大小
* @return 去除填充后的数据,如果isZeroPadding为false或长度刚好,返回原数据
*/
private byte[] removePadding(byte[] data, int blockSize) {
if (this.isZeroPadding()) {
final int length = data.length;
final int remainLength = length % blockSize;
if (remainLength == 0) {
// 解码后的数据正好是块大小的整数倍,说明可能存在补0的情况,去掉末尾所有的0
int i = length - 1;
while (i >= 0 && 0 == data[i]) {
i--;
}
return SecureUtil.resize(data, i + 1);
}
}
return data;
}
// --------------------------------------------------------------------------------- Private method end
// --------------------------------------- getter/setter --------------------------------------------------------//
public Cipher getCipher() {
return cipher;
}
public void setCipher(Cipher cipher) {
this.cipher = cipher;
}
public boolean isZeroPadding() {
return zeroPadding;
}
public void setZeroPadding(boolean zeroPadding) {
this.zeroPadding = zeroPadding;
}
}