背景
公司为了提高用户对 APP 的粘性,同时也为了能多一些营收,去年开始在 APP 内接入有赞商城,今年开始售卖打印机及提供打印机的配对、连接、打印功能。
目前为止,接入了4、5款打印机,基本每一款打印机都有自己的 SDK,接入过程麻烦了点,但国内上架时一直没在这方面遇到问题,国外的话,因为一些我也不明白的政策问题,没有提供商城及打印功能,这些模块在打包时就已经从项目中剔除了。
最近,产品经理兼商城客服向我们提需求,国外也有用户购买了打印机,希望打印功能可以在国外开放,毕竟,相对国内来说,国外的 Android 用户付费意愿更强,这样一想需求也挺合理。
实现就很简单了,改几个判断条件和模块配置,上架就OK了。
但,之前上架 Google Play Store📱时出现过 OpenSSL 过低导致审核不通过的问题,当然 Play Store 也提供了 OpenSSL 相关的拒绝原因及解决方法。当时从 build/outputs/mapping/xxxRelease/usage.txt
中全局搜索“OpenSSL” 关键字发现,居然是有一款打印机的 SDK 依赖了低版本的 OpenSSL。而那时候国外不应该有打印功能,我们出现的问题是,虽然 APP 代码中屏蔽了国外的打印功能入口,但打包时没有剔除掉打印模块。
那时候剔除掉包含 OpenSSL 但国外并不需要的模块就能解决问题,而现在需求变动,这个问题也必须要解决了。
问题分析及解决过程 📚
问题🤔
打印机 SDK 是以 AAR 形式提供的,AAR 中 OpenSSL 是以 so 形式引入并用 JNI 调用的,如何升级 OpenSSL 版本但不影响原本功能呢。
思路⚡️
方法1: 请求 SDK 提供方协助。由于我们双方只是商业合作关系,没有利益往来,所以对方也是爱答不理。
方法2: 替换 AAR 中的 SO然后重打包。由于没有现成 SO,需要自己编译,费时费力。
方法3: 改为 Java 实现。最终方案。
解决🍵
之所以使用方法三,是因为通过 Jadx 分析后发现,AAR 内部 JNI 只用到了 OpenSSL 中的rsaCryptPublic()
方法,而这完全可以用 Java 实现。
- 分析 AAR 结构
OpenSSL 方法的入口其实只在一个名叫xxxUtils
的 Class 中,我们可以自己写一个xxxUtils
,里面用 Java 实现一个 rsaCryptPublic()
,将其编译为 Class后,替换掉 AAR 中的同名 Class 就可以了。
- 完成 AAR 重打包
- 终端使用
unzip
解压缩 aar 到桌面一个文件夹中,例如newaar
-
mkdir ~/Desktop/newjar & cd /Desktop/newjar & jar -xvf classes.jar
解压缩 jar 到/Desktop/newjar
- 编写新的 Java 类
-
javac xxxUtils.java
编译 java 为 class - 新 Class 替换 旧 Class
-
cd ~/Desktop/newjar & jar -cvf classes.jar ./
压缩newjar
中所有文件到classes.jar
- 将新生成的
classes.jar
替换原先的~/Desktop/newaar/classes.jar
- 全选
newaar
中的所有文件,压缩为zip,然后重命名为aar,搞定
分享 🎁
OpenSSL 中的rsaCryptPublic()
作用为输入一个私钥加密后的 byte 数组,使用公钥解密后再以 byte 数组形式返回。
公私钥的获取,我这边用 strings libencryption.so
命令跑了一下就出来了,得益于 SDK 提供方没有做一些加密措施😄。
Java 实现:
public static byte[] rsaCryptPublic(byte[] str) {
try {
//base64编码的公钥
byte[] decoded = Base64.getDecoder().decode(getPublicKeyString());
PublicKey pubKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));
//RAS 解密
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, pubKey);
return cipher.doFinal(str);
} catch (Exception e) {
e.printStackTrace();
return new byte[0];
}
}