一、背景
项目中需要对敏感字段进行加密,但是加密方法中涉及到比较复杂的加密算法,这些算法都需要一个Provider,主要是用于初始化算法的。
以下是遇到的具体问题
二、报错截图
java.security.NoSuchAlgorithmException: Cannot find any provider supporting RSA/ECB/PKCS1Padding
三、报错原因
原因是我们运行的jre不支持,对方使用的是JDK8,而我们的是JDK11
不仅出现了上面的问题,而且,对于对方已经加密的字符串,我们无法正常解析。
那这两者有什么区别呢?估计是模块化的区别。
见文章
Java9的新特性模块化(Module)
四、问题解决
4.1 Step1、引入POM文件
<!-- https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk14</artifactId>
<version>1.46</version>
<scope>compile</scope>
</dependency>
4.2 Step2、Security.addProvider
加入BouncyCastleProvider,该类属于org.bouncycastle.jce.provider包
调用的方法面前加入这段方法即可,用于初始化算法
//初始化
Security.addProvider(new BouncyCastleProvider());
4.2.1 Security.addProvider()方法的具体详情如下:
包路径:java.security.Security (提供安全框架的类和接口。)
类名称:Security
方法名:addProvider
五、 样例代码
5.1 案例代码:SymAlgoUtils.messageCrypt
public static String getActualVin(String vinCryptoCipher) {
byte[] vinCryptoKey = new byte[0];
String vinCryptoPlainHexStr = null;
try {
Security.addProvider(new BouncyCastleProvider());
vinCryptoKey = KDFUtils.genVinCryptoKey();
vinCryptoPlainHexStr = SymAlgoUtils.messageCrypt(Cipher.DECRYPT_MODE, vinCryptoKey, BasicConvertUtils.hexString2byteArray(vinCryptoCipher));
} catch (Exception e) {
log.error("getActualVin fail! decrypt error ,vinCryptoCipher= 【{}】, vinCryptoKey =【{}】, e=【{}】",
vinCryptoCipher, vinCryptoKey, e.getMessage());
return vinCryptoCipher;
}
if (StrUtil.isBlank(vinCryptoPlainHexStr)) {
log.error("getActualVin fail! vinCryptoPlainHexStr = 【{}】 is blank", vinCryptoCipher);
return vinCryptoCipher;
}
// 解密后可见的明文VIN码
return BasicConvertUtils.hexStr2Str(vinCryptoPlainHexStr);
}
5.2 案例代码:AES加密工具类
import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.HexUtil;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.stereotype.Component;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Security;
import java.util.Arrays;
//AES加密工具类
@Component
public class AesUtil {
/**
* AES128 算法
* CBC 模式
* PKCS7Padding 填充模式
* CBC模式需要添加一个参数iv
* 介于java 不支持PKCS7Padding,只支持PKCS5Padding
* 但是PKCS7Padding 和 PKCS5Padding 没有什么区别
*/
private Key key;
private Cipher cipher;
String aesKey = "1111bat1977";
byte[] iv = "7773066605060999".getBytes();
byte[] keyBytes = aesKey.getBytes();
public void init(byte[] keyBytes) {
// 如果密钥不足16位,那么就补足. 这个if 中的内容很重要
int base = 16;
if (keyBytes.length % base != 0) {
int groups = keyBytes.length / base + (keyBytes.length % base != 0 ? 1 : 0);
byte[] temp = new byte[groups * base];
Arrays.fill(temp, (byte) 0);
System.arraycopy(keyBytes, 0, temp, 0, keyBytes.length);
keyBytes = temp;
}
// 初始化
Security.addProvider(new BouncyCastleProvider());
// 转化成JAVA的密钥格式
key = new SecretKeySpec(keyBytes, CipherType.AES_ALGORITHM);
try {
// 初始化cipher
cipher = Cipher.getInstance(CipherType.AES_CBC_PKC7PADDING, "BC");
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchPaddingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoSuchProviderException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public String encrypt(String content) {
return Base64.encode(encrypt(content.getBytes(), keyBytes));
}
public String encrypt(String content, String keyBytes) {
return Base64.encode(encrypt(content.getBytes(), keyBytes.getBytes()));
}
public String encryptByHex(String content, String keyBytes) {
return HexUtil.encodeHexStr(encrypt(content.getBytes(), keyBytes.getBytes()));
}
public String encryptByHex(String content) {
return HexUtil.encodeHexStr(encrypt(content.getBytes(), keyBytes));
}
/**
* 加密方法
* @param content 要加密的字符串
* @param keyBytes 加密密钥
* @return
*/
public byte[] encrypt(byte[] content, byte[] keyBytes) {
byte[] encryptedText = null;
keyBytes = new String(keyBytes).getBytes();
init(keyBytes);
try {
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
encryptedText = cipher.doFinal(content);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return encryptedText;
}
public String decrypt(String encryptedData) {
return new String(decrypt(Base64.decode(encryptedData), keyBytes));
}
public String decrypt(String encryptedData, String keyData) {
return new String(decrypt(Base64.decode(encryptedData), keyData.getBytes()));
}
public String decryptByHex(String encryptedData, String keyData) {
return new String(decrypt(HexUtil.decodeHex(encryptedData), keyData.getBytes()));
}
public String decryptByHex(String encryptedData) {
return new String(decrypt(HexUtil.decodeHex(encryptedData), keyBytes));
}
/**
* 解密方法
* @param encryptedData 要解密的字符串
* @param keyBytes 解密密钥
*/
public byte[] decrypt(byte[] encryptedData, byte[] keyBytes) {
byte[] encryptedText = null;
init(keyBytes);
try {
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
encryptedText = cipher.doFinal(encryptedData);
} catch (Exception e) {
e.printStackTrace();
}
return encryptedText;
}
public static void main(String[] args) {
AesUtil aes = new AesUtil();
//加密字符串
String content = "{\"userName\":\"12\",\"userPhone\":\"123\"}";
System.out.println("加密前的:" + content);
// 加密方法
String encStr = aes.encryptByHex(content);
System.out.println("加密后的内容:" + encStr);
// 解密方法
String decStr = aes.decryptByHex(encStr);
System.out.println("解密后的内容:" + decStr);
}
}
加密前的:{"userName":"12","userPhone":"123"}
加密后的内容:989f16833c907f8168a6dc81424f136726a2fdf587f83360d8e24ef4506ffbfd69f0a7453a97dcc609acb3d312b4bf77
解密后的内容:{"userName":"12","userPhone":"123"}