由一次Base64编码异常引起的小bug
base64是一种基本的加密算法,在Java中可以使用java自带的base64编码,也可以用apache 的commons-codec包。最近在使用commons-codec 1.10 版本能正常解密微信的消息,升级为1.13后出现了不能正常decode,出现异常
| 1 | java.lang.IllegalArgumentException: Last encoded character (before the paddings if any) is a valid base 64 alphabet but not a possible value | 
- 具体场景 - 处理微信公众号消息时,对消息内容进行必须的加解密,出现的问题是处理aesKey时出现的,具体demo如下 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14- public WxOpenCryptUtil(WxOpenConfigStorage wxOpenConfigStorage) { 
 /*
 * @param token 公众平台上,开发者设置的token
 * @param encodingAesKey 公众平台上,开发者设置的EncodingAESKey
 * @param appId 公众平台appid
 */
 String encodingAesKey = wxOpenConfigStorage.getComponentAesKey();
 String token = wxOpenConfigStorage.getComponentToken();
 String appId = wxOpenConfigStorage.getComponentAppId();
 this.token = token;
 this.appidOrCorpid = appId;
 this.aesKey = Base64.decodeBase64(encodingAesKey + "=");
 }- 当升级commons-codec版本为1.13及以上时,会出现上述异常 
- 出现的原因 - 1.13出现异常的方法 - 1 
 2
 3
 4
 5
 6
 7- private long validateCharacter(final int numBitsToDrop, final Context context) { 
 if ((context.ibitWorkArea & numBitsToDrop) != 0) {
 throw new IllegalArgumentException(
 "Last encoded character (before the paddings if any) is a valid base 64 alphabet but not a possible value");
 }
 return context.ibitWorkArea >> numBitsToDrop;
 }- 仔细分析可以看出编解码是在 - BaseNCodec.java是Base64和Base32的基类 - 可以看出唯一的差别就是在解码时对参数做了校验。有必要了解下这个参数校验做了些什么? - 1 - context.ibitWorkArea & numBitsToDrop - ibitWorkArea: 位处理的基本位数 - numBitsToDrop: 应该为空的低位数目 - 可以看出当 - context.ibitWorkArea & numBitsToDrop不为0时就会抛出异常,实际上只有base64严格模式编码下,才可能会为0,松散模式不会为0
- 解决办法 - 降低版本到1.12以下可以解决该问题,或者等commons-codec版本更新到1.15,最新的源码已经处理了该问题 - 1 
 2
 3
 4
 5
 6
 7- private void validateCharacter(final int emptyBitsMask, final Context context) { 
 if (isStrictDecoding() && (context.ibitWorkArea & emptyBitsMask) != 0) {
 throw new IllegalArgumentException(
 "Strict decoding: Last encoded character (before the paddings if any) is a valid base 64 alphabet but not a possible encoding. " +
 "Expected the discarded bits from the character to be zero.");
 }
 }
- 扩展 - base64的严格模式和松散模式定义,直接引用源码了 - Lenient: Any trailing bits are composed into 8-bit bytes where possible. 
 The remainder are discarded.
 Strict: The decoding will raise an {@link IllegalArgumentException} if trailing bits
 are not part of a valid encoding. Any unused bits from the final character must
 be zero. Impossible counts of entire final characters are not allowed.
References
使用java8的java.util.Base64报“java.lang.IllegalArgumentException: Illegal base64 character d”的问题
Base64.decode fails on Java11 for certain valid base 64 encoded String