问题的产生
报错代码运行环境
JDK:Oracle JDK17
CentOS7.8
这个问题刚拿到比较棘手。原因是本地windows是OK的,centos上是不成功的,报了下面的错误:
Caused by: java.lang.SecurityException: JCE cannot authenticate the provider BC
at java.base/javax.crypto.Cipher.getInstance(Cipher.java:722)
at cn.hutool.crypto.SecureUtil.createCipher(SecureUtil.java:1032)
... 72 common frames omitted
问题的背景
说问题的背景,主要是因为现有系统中需要对小程序自动登录的相关数据进行解密。网络上将小程序使用的是Pkcs7Padding的方式。
问题的解决方案
解决方案一
按照网上的解决思路,貌似能解决问题,但是感觉不是最好的办法。
1、在{jdk目录}目录下创建jre/lib目录
2、复制bcmail-jdk16-1.46.jar bcprov-jdk16-1.46.jar文件放入当前目录下
(添加 bcprov-jdk16-146.jar, bcmail-jdk16.143 到 /path_to_your_jvm/jre/lib/ext)
3、在/etc/profile文件下CLASSPATH添加jre相关目录
4、在{jdk目录}/conf/security/java.security
添加下面命令
security.provider.13=org.bouncycastle.jce.provider.BouncyCastleProvider
security.provider.13 这个根据上面的排序来,到几就是几,不一定是13。
5、如果还有问题的话,在maven项目中引入bcmail-jdk16-1.46.jar bcprov-jdk16-1.46.jar两个文件。
运行后,在后台debug可以看到正常解密了。
但是这种改法对JVM的侵入比较深。
解决方案二
我根据实际情况,使用了下面的这种方法。
将PKCS7Padding修改为PKCS5Padding,其他都不变,测试发现代码解密是OK的。
Cipher.getInstance("AES/CBC/Pkcs5Padding")
问题的分析
Java中的填充算法(Cipher)
算法名 | 描述 |
---|---|
NoPadding | 不填充 |
ISO10126Padding | W3C的“XML加密语法和处理”文档中的5.2块加密算法描述了块密码的填充。 |
OAEPPadding, OAEPWithAndPadding | PKCS1中定义的最佳非对称加密填充方案,其中<摘要>应被消息摘要替换,<mgf>应被掩码生成函数替换。示例:OAEPWithMD5AndMGF1填充和OAEPWithSHA-512AndMGF1填充。 如果使用OAEPAdding,则使用ajavax.crypto.spec.OAEPParameterSpec对象初始化Cipher对象,以支持OAEPAddition所需的值。 |
PKCS1Padding | PKCS1中描述的填充方案,与RSA算法一起使用。 |
PKCS5Padding | “PKCS5:基于密码的加密标准”1.5版,1993年11月RSA Laboratories描述了填充方案。 |
SSL3Padding | SSL协议3.0版(1996年11月18日)第5.2.3.2节(CBC分组密码)中定义的填充方案:块加密结构{不透明含量[SSL压缩长度];不透明MAC[CipherSpec.hash_size];uint8填充[GenericBlockCipher.padding_length];uint8填充长度;}GenericBlockCipher;GenericBlockCipher实例的大小必须是块密码的块长度的倍数。始终存在的填充长度有助于填充,这意味着如果:sizeof(内容)+sizeof(MAC)%block_length=0,由于存在填充长度,填充长度必须为(block_length-1)字节。这使得填充方案与PKCS5Padding相似(但不完全相同),其中填充长度在填充中编码(范围从1到block_length)。使用SSL方案,sizeof(padding)以始终存在的padding_length编码,因此范围从0到block_length-1。 |
JDK本身是不支持PKCS7Padding的。
不支持的原因可能也和JAVA对块大小的支持有一定的关系。在PKCS5Padding中,明确定义Block的大小是8位,而在PKCS7Padding定义中,对于块的大小是不确定的,可以在1-255之间(块长度超出255的尚待研究),填充值的算法都是一样的:
value=k - (l mod k) ,K=块大小,l=数据长度,如果l=8, 则需要填充额外的8个byte的8。
理论上来讲,用PKCS7Padding加密,使用PKCS5Padding解密也是可以行得通的。
根据这个思路,和对国内的一般的实现,基本上都会调用默认的方式,这样基本上PKCS5Padding和PKCS7Padding的使用不存在异样的。这样JDK17兼容就很简单的修改了(这里后面可能会存在问题,但是我想JDK不实现PKCS7Padding,应该也是它本身存在一定的问题)