由一次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
14public 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
7private 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
7private 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