记一次对接小程序时遇到的加密问题“Cannot find any provider supporting AES/CBC/PKCS7Padding”
前情提要:
依然是先碎碎念一下,这个问题是早几个月前,和我们小程序开发对接时候遇到的,并且解决后当时就打算写博客记一下,大致的保存了下资料,但是当时快下班了嘛,就想着改天再说。。。。。。然后人太咸鱼了,这等我下次扑腾一下已经是几个月后的今天了,明明上班不少很闲的时间,结果都拿来和项目经理一起开黑上王者了。。罪过罪过。
然后说说具体问题,这个依稀记得是因为获取小程序的用户手机号,要对加密内容进行解密,原文是这么写的
加密数据解密算法 接口如果涉及敏感数据(如wx.getUserInfo当中的 openId 和 unionId),接口的明文内容将不包含这些敏感数据。开发者如需要获取敏感数据,需要对接口返回的加密数据(encryptedData) 进行对称解密。 解密算法如下:
对称解密使用的算法为 AES-128-CBC,数据采用PKCS#7填充。 对称解密的目标密文为 Base64_Decode(encryptedData)。 对称解密秘钥 aeskey = Base64_Decode(session_key), aeskey 是16字节。 对称解密算法初始向量 为Base64_Decode(iv),其中iv由数据接口返回。
然后问题就出在这个 PKCS#7 填充这个地方,我用的jdk是1.8的,报错一直提示“Cannot find any provider supporting AES/CBC/PKCS7Padding”,经查验,本身java只支持到 PKCS5 填充,这个PKCS7 默认是不支持的,好像是涉及到了AES256加密什么的,长度过程被限制了。
处理方法:
首先,解密的代码中加上这一行:Security.addProvider(new BouncyCastleProvider());
然后说一个别的地方看见的解决办法,内容如下。。。另外不是我不想贴来源,实在是几个月前的我真的找不到从哪找的文字了~
在这段代码可以运行之前,还有一个问题需要解决。 Java本身限制密钥的长度最多128位,而AES256需要的密钥长度是256位,因此需要到Java官网上下载一个Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files。 官方网站提供了JCE无限制权限策略文件的下载: JDK6的下载地址: http://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html JDK7的下载地址: http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html JDK8的下载地址: http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html 下载后解压,可以看到local_policy.jar和US_export_policy.jar以及readme.txt。 如果安装了JRE,将两个jar文件放到%JRE_HOME%\lib\security下覆盖原来文件,记得先备份。 如果安装了JDK,将两个jar文件也放到%JDK_HOME%\jre\lib\security下。
另外再说一个处理办法:
其实我们1.8的jdk(具体哪个版本开始不知道)中自带了这个无限制策略文件的,
比如我的目录是:E:\jdk-ALL\jdk1.8.0_152\jre\lib\security\policy,在这个文件夹下能看见两个文件夹,分别是“limited”和“unlimited”,两个文件夹下面的内容都是“local_policy.jar和US_export_policy.jar”这两个东西,我们要取用的是“unlimited”下的jar
如果你是要给linux上的jdk进行处理,则可在自己电脑上将其取出,然后去linux服务器上对它进行覆盖即可,记得做好备份。参考路径如:/usr/local/java/jdk1.8.0_131/jre/lib/security
如果你是要给windows上的jdk进行处理,则直接在java.security这个文件中,将“#crypto.policy=unlimited”这段话取消注释即可,该文件所在参考路径如:E:\jdk-ALL\jdk1.8.0_152\jre\lib\security
最后:
实话说我也记不清是不是这样就行了,但是我自己印象中是这样就完事了,如果说谁看见了,发现还是没解决了,麻烦给我留下言,谢谢。。。。。。另外希望我以后可别再这么懒了,好歹要写的博客周末给它写掉,时间一长都忘记完了。
参考代码:
/** * 对称解密使用的算法为 AES-128-CBC,数据采用PKCS#7填充。 * 对称解密的目标密文为 Base64_Decode(encryptedData)。 * 对称解密秘钥 aeskey = Base64_Decode(session_key), aeskey 是16字节。 * 对称解密算法初始向量 为Base64_Decode(iv),其中iv由数据接口返回。 */ String sessionKey = json.getString("session_key").trim(); String encryData = json.getString("encry_data").trim(); String iv = json.getString("iv").trim(); //解密手机号 Security.addProvider(new BouncyCastleProvider());//因为jdk本身只支持pkcs5,加代码以及修改jdk配置支持pkcs7 Base64 base64 = new Base64(); byte[] content = base64.decode(encryData); byte[] aesKey = base64.decode(sessionKey); byte[] ivs = base64.decode(iv); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding"); SecretKeySpec skeySpec = new SecretKeySpec(aesKey, "AES"); cipher.init(Cipher.DECRYPT_MODE, skeySpec, new IvParameterSpec(ivs)); byte[] result = cipher.doFinal(content); JSONObject caller = JSONObject.parseObject(new String(result).toLowerCase()); String xcxMobile = caller.getString("phonenumber");//解密出小程序手机号
上述代码中的json是前端传过来的json参数,这三个参数就是小程序那边取到的。
Base64 用的包是org.apache.commons.codec.binary.Base64
Security 用的包是java.security.Security