package com.tws.plugin.util; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.lang.ref.WeakReference; import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; import java.util.Enumeration; import java.util.HashSet; import java.util.jar.JarEntry; import java.util.jar.JarFile; import tws.component.log.TwsLog; import android.content.pm.Signature; /** * Copy from Android SDK */ public class PackageVerifyer { private static String TAG = "PackageVerifyer"; private static boolean DEBUG = false; private static final Object mSync = new Object(); private static WeakReference<byte[]> mReadBuffer; /** * @param sourcePath * @param simpleMode * @return */ public static Signature[] collectCertificates(String sourcePath, boolean simpleMode) { // 其实可以直接通过getPackageManager().getPackageArchiveInfo()获取插件的签名信息 // 不用这么麻烦 Signature mSignatures[] = null; WeakReference<byte[]> readBufferRef; byte[] readBuffer = null; synchronized (mSync) { readBufferRef = mReadBuffer; if (readBufferRef != null) { mReadBuffer = null; readBuffer = readBufferRef.get(); } if (readBuffer == null) { readBuffer = new byte[8192]; readBufferRef = new WeakReference<byte[]>(readBuffer); } } try { JarFile jarFile = new JarFile(sourcePath); Certificate[] certs = null; if (simpleMode) { // if SIMPLE MODE,, then we // can trust it... we'll just use the AndroidManifest.xml // to retrieve its signatures, not validating all of the // files. JarEntry jarEntry = jarFile.getJarEntry("AndroidManifest.xml"); certs = loadCertificates(jarFile, jarEntry, readBuffer); if (certs == null) { TwsLog.e(TAG, "Package " + " has no certificates at entry " + jarEntry.getName() + "; ignoring!"); jarFile.close(); return null; } if (DEBUG) { TwsLog.d(TAG, "File " + sourcePath + ": entry=" + jarEntry + " certs=" + (certs != null ? certs.length : 0)); if (certs != null) { final int N = certs.length; for (int i = 0; i < N; i++) { TwsLog.d( TAG, " Public key: " + certs[i].getPublicKey().getEncoded() + " " + certs[i].getPublicKey()); } } } } else { Enumeration entries = jarFile.entries(); while (entries.hasMoreElements()) { JarEntry je = (JarEntry) entries.nextElement(); if (je.isDirectory()) continue; if (je.getName().startsWith("META-INF/")) continue; Certificate[] localCerts = loadCertificates(jarFile, je, readBuffer); if (DEBUG) { TwsLog.d(TAG, "File " + sourcePath + " entry " + je.getName() + ": certs=" + certs + " (" + (certs != null ? certs.length : 0) + ")"); } if (localCerts == null) { TwsLog.e(TAG, "Package " + " has no certificates at entry " + je.getName() + "; ignoring!"); jarFile.close(); return null; } else if (certs == null) { certs = localCerts; } else { // Ensure all certificates match. for (int i = 0; i < certs.length; i++) { boolean found = false; for (int j = 0; j < localCerts.length; j++) { if (certs[i] != null && certs[i].equals(localCerts[j])) { found = true; break; } } if (!found || certs.length != localCerts.length) { TwsLog.e(TAG, "Package " + " has mismatched certificates at entry " + je.getName() + "; ignoring!"); jarFile.close(); return null; } } } } } jarFile.close(); synchronized (mSync) { mReadBuffer = readBufferRef; } if (certs != null && certs.length > 0) { final int N = certs.length; mSignatures = new Signature[certs.length]; for (int i = 0; i < N; i++) { mSignatures[i] = new Signature(certs[i].getEncoded()); } } else { TwsLog.e(TAG, "Package " + " has no certificates; ignoring!"); return null; } } catch (CertificateEncodingException e) { TwsLog.e(TAG, "Exception reading " + sourcePath, e); return null; } catch (IOException e) { TwsLog.e(TAG, "Exception reading " + sourcePath, e); return null; } catch (RuntimeException e) { TwsLog.e(TAG, "Exception reading " + sourcePath, e); return null; } return mSignatures; } private static Certificate[] loadCertificates(JarFile jarFile, JarEntry je, byte[] readBuffer) { try { // We must read the stream for the JarEntry to retrieve // its certificates. InputStream is = new BufferedInputStream(jarFile.getInputStream(je)); while (is.read(readBuffer, 0, readBuffer.length) != -1) { // not using } is.close(); return je != null ? je.getCertificates() : null; } catch (IOException e) { TwsLog.e(TAG, "Exception reading " + je.getName() + " in " + jarFile.getName(), e); } catch (RuntimeException e) { TwsLog.e(TAG, "Exception reading " + je.getName() + " in " + jarFile.getName(), e); } return null; } public static boolean isSignaturesSame(Signature[] s1, Signature[] s2) { if (s1 == null) { return false; } if (s2 == null) { return false; } HashSet<Signature> set1 = new HashSet<Signature>(); for (Signature sig : s1) { set1.add(sig); } HashSet<Signature> set2 = new HashSet<Signature>(); for (Signature sig : s2) { set2.add(sig); } // Make sure s2 contains all signatures in s1. if (set1.equals(set2)) { return true; } return false; } }