背景
公司為了提高用戶對(duì) APP 的粘性,同時(shí)也為了能多一些營(yíng)收,去年開(kāi)始在 APP 內(nèi)接入有贊商城,今年開(kāi)始售賣(mài)打印機(jī)及提供打印機(jī)的配對(duì)、連接、打印功能。
目前為止,接入了4、5款打印機(jī),基本每一款打印機(jī)都有自己的 SDK,接入過(guò)程麻煩了點(diǎn),但國(guó)內(nèi)上架時(shí)一直沒(méi)在這方面遇到問(wèn)題,國(guó)外的話,因?yàn)橐恍┪乙膊幻靼椎恼邌?wèn)題,沒(méi)有提供商城及打印功能,這些模塊在打包時(shí)就已經(jīng)從項(xiàng)目中剔除了。
最近,產(chǎn)品經(jīng)理兼商城客服向我們提需求,國(guó)外也有用戶購(gòu)買(mǎi)了打印機(jī),希望打印功能可以在國(guó)外開(kāi)放,畢竟,相對(duì)國(guó)內(nèi)來(lái)說(shuō),國(guó)外的 Android 用戶付費(fèi)意愿更強(qiáng),這樣一想需求也挺合理。
實(shí)現(xiàn)就很簡(jiǎn)單了,改幾個(gè)判斷條件和模塊配置,上架就OK了。
但,之前上架 Google Play Store??時(shí)出現(xiàn)過(guò) OpenSSL 過(guò)低導(dǎo)致審核不通過(guò)的問(wèn)題,當(dāng)然 Play Store 也提供了 OpenSSL 相關(guān)的拒絕原因及解決方法。當(dāng)時(shí)從 build/outputs/mapping/xxxRelease/usage.txt 中全局搜索“OpenSSL” 關(guān)鍵字發(fā)現(xiàn),居然是有一款打印機(jī)的 SDK 依賴(lài)了低版本的 OpenSSL。而那時(shí)候國(guó)外不應(yīng)該有打印功能,我們出現(xiàn)的問(wèn)題是,雖然 APP 代碼中屏蔽了國(guó)外的打印功能入口,但打包時(shí)沒(méi)有剔除掉打印模塊。
那時(shí)候剔除掉包含 OpenSSL 但國(guó)外并不需要的模塊就能解決問(wèn)題,而現(xiàn)在需求變動(dòng),這個(gè)問(wèn)題也必須要解決了。
問(wèn)題分析及解決過(guò)程 ??
問(wèn)題??

打印機(jī) SDK 是以 AAR 形式提供的,AAR 中 OpenSSL 是以 so 形式引入并用 JNI 調(diào)用的,如何升級(jí) OpenSSL 版本但不影響原本功能呢。
思路??
方法1: 請(qǐng)求 SDK 提供方協(xié)助。由于我們雙方只是商業(yè)合作關(guān)系,沒(méi)有利益往來(lái),所以對(duì)方也是愛(ài)答不理。
方法2: 替換 AAR 中的 SO然后重打包。由于沒(méi)有現(xiàn)成 SO,需要自己編譯,費(fèi)時(shí)費(fèi)力。
方法3: 改為 Java 實(shí)現(xiàn)。最終方案。
解決??
之所以使用方法三,是因?yàn)橥ㄟ^(guò) Jadx 分析后發(fā)現(xiàn),AAR 內(nèi)部 JNI 只用到了 OpenSSL 中的rsaCryptPublic()方法,而這完全可以用 Java 實(shí)現(xiàn)。
- 分析 AAR 結(jié)構(gòu)
OpenSSL 方法的入口其實(shí)只在一個(gè)名叫xxxUtils的 Class 中,我們可以自己寫(xiě)一個(gè)xxxUtils,里面用 Java 實(shí)現(xiàn)一個(gè) rsaCryptPublic(),將其編譯為 Class后,替換掉 AAR 中的同名 Class 就可以了。
- 完成 AAR 重打包
- 終端使用
unzip解壓縮 aar 到桌面一個(gè)文件夾中,例如newaar -
mkdir ~/Desktop/newjar & cd /Desktop/newjar & jar -xvf classes.jar解壓縮 jar 到/Desktop/newjar - 編寫(xiě)新的 Java 類(lèi)
-
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()作用為輸入一個(gè)私鑰加密后的 byte 數(shù)組,使用公鑰解密后再以 byte 數(shù)組形式返回。
公私鑰的獲取,我這邊用 strings libencryption.so命令跑了一下就出來(lái)了,得益于 SDK 提供方?jīng)]有做一些加密措施??。
Java 實(shí)現(xiàn):
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];
}
}