聊城资文网
当前位置:首页»时尚创意»安全

插件化阿里Atlas之插件热补丁安全校验

通过云端下发插件补丁的方式,我们可以很方便的用来修复线上bug和发布新功能。但是如何保证接收到的补丁是安全的呢?很多人想到使用类似校验md5的方式,但是md5值也是通过接口下发,本来就是不安全的,使用fiddler mock接口,md5值想……

专题: android签名文件 高领条纹内搭 小学开学安全教育教案 时髦女款毛呢大衣 

通过云端下发插件补丁的方式,我们可以很方便的用来修复线上bug和发布新功能。但是如何保证接收到的补丁是安全的呢?

很多人想到使用类似校验md5的方式,但是md5值也是通过接口下发,本来就是不安全的,使用fiddler mock接口,md5值想返回什么都行。

其实安卓的apk也是有签名的,假如补丁包也能使用apk相同的证书签名,然后对比下发补丁的签名和当前运行的apk签名是否一致就行了。

1、对补丁进行zip压缩

atlas的插件补丁文件有2个,一个是update.json,另一个是tpatch文件。对这两个文件进行压缩得到的zip文件名是patch.zip

2、使用apk的release证书对patch.zip签名

执行以下签名命令:

jarsigner -verbose -keystore KEYSTORE_FILEPATH -signedjar patch_v.zip patch.zip dengyin2000

-keystore 后面输入apk证书的路径-signedjar 后面输入签名完成后的zip文件名,然后是需要签名的文件名,然后是证书的别名签名成功后,你就能看到patch_v.zip文件了。

3、使用代码进行验证,SecurityChecker.java

看看代码:

package com.iteye.dengyin2000.android.securitychecker; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.util.Log; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.security.PublicKey; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.Enumeration; import java.util.jar.JarEntry; import java.util.jar.JarFile; import javax.security.auth.x500.X500Principal; /** * Created by denny on 2017/5/26. */ public class SecurityChecker { private static final String TAG = "SecurityChecker"; private static final String CLASSES_DEX = "classes.dex"; private static final X500Principal DEBUG_DN = new X500Principal( "CN=Android Debug,O=Android,C=US"); private final Context mContext; /** * host publickey */ private PublicKey mPublicKey; /** * host debuggable */ private boolean mDebuggable; public SecurityChecker(Context context) { mContext = context; init(mContext); } public boolean verifyAtlasPatch(File path) { JarFile jarFile = null; try { jarFile = new JarFile(path); Enumeration entries = jarFile.entries(); while (entries.hasMoreElements()) { JarEntry jarEntry = entries.nextElement(); if ("update.json".equals(jarEntry.getName()) || jarEntry.getName().endsWith(".tpatch")) { loadDigestes(jarFile, jarEntry); Certificate[] certs = jarEntry.getCertificates(); if (certs == null) { return false; } if (!check(path, certs)) { return false; } Log.i(TAG, "File: " + jarEntry.getName() + " is verified"); } } return true; } catch (IOException e) { Log.e(TAG, path.getAbsolutePath(), e); return false; } finally { try { if (jarFile != null) { jarFile.close(); } } catch (IOException e) { Log.e(TAG, path.getAbsolutePath(), e); } } } /** * @param path Apk file * @return true if verify apk success */ public boolean verifyApk(File path) { if (mDebuggable) { Log.d(TAG, "mDebuggable = true"); return true; } JarFile jarFile = null; try { jarFile = new JarFile(path); JarEntry jarEntry = jarFile.getJarEntry(CLASSES_DEX); if (null == jarEntry) {// no code return false; } loadDigestes(jarFile, jarEntry); Certificate[] certs = jarEntry.getCertificates(); return certs != null && check(path, certs); } catch (IOException e) { Log.e(TAG, path.getAbsolutePath(), e); return false; } finally { try { if (jarFile != null) { jarFile.close(); } } catch (IOException e) { Log.e(TAG, path.getAbsolutePath(), e); } } } private void loadDigestes(JarFile jarFile, JarEntry je) throws IOException { InputStream is = null; try { is = jarFile.getInputStream(je); byte[] bytes = new byte[8192]; while (is.read(bytes) > 0) { } } finally { if (is != null) { is.close(); } } } // verify the signature of the Apk private boolean check(File path, Certificate[] certs) { if (certs.length > 0) { for (int i = certs.length - 1; i >= 0; i--) { try { certs[i].verify(mPublicKey); return true; } catch (Exception e) { Log.e(TAG, path.getAbsolutePath(), e); } } } return false; } // initialize,and check debuggable private void init(Context context) { try { PackageManager pm = context.getPackageManager(); String packageName = context.getPackageName(); PackageInfo packageInfo = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES); CertificateFactory certFactory = CertificateFactory .getInstance("X.509"); ByteArrayInputStream stream = new ByteArrayInputStream( packageInfo.signatures[0].toByteArray()); X509Certificate cert = (X509Certificate) certFactory .generateCertificate(stream); mDebuggable = cert.getSubjectX500Principal().equals(DEBUG_DN); mPublicKey = cert.getPublicKey(); } catch (NameNotFoundException e) { Log.e(TAG, "init", e); } catch (CertificateException e) { Log.e(TAG, "init", e); } } }

init方法,获得运行apk的签名publicKey。verifyAtlasPatch方法,验证zip包里面的update.json和.tpatch文件的签名是否跟apk的签名一致。所以验证补丁包是否合法调用这个方法就行了。:)

本文关键字:安全    

您至少需要输入5个字

相关内容

编辑精选

copyright © 2017 http://www.lcdzc.cn 聊城资文网 版权所有