记得上次在公司玩微信支付的时候,微信的商务给了一个破apk获取我们自己开发的app的公钥,非常之难用,后来自己写了一个方法,再后来找不见了,
这几天决定把腾讯的开放平台都研究个遍,提交app的时候,又需要提供公钥,那就动手吧:
不说原理直接发代码:
package com.example.navigatedemo; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; import java.util.ArrayList; import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.security.cert.X509Certificate; import android.app.Activity; import android.content.pm.PackageManager; import android.content.pm.Signature; import android.os.Bundle; import android.os.Environment; import android.util.DisplayMetrics; import android.util.Log; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //获取签名 File file=new File(Environment.getExternalStorageDirectory()+"/XXXbyfayuV1.3.apk"); getAPKSignatures(Environment.getExternalStorageDirectory()+"/SOUAPP_babyfayuV1.3.apk"); } /** * 从APK中读取签名 * @param file * @return * @throws IOException */ private static ListgetSignaturesFromApk(File file) throws IOException { List signatures=new ArrayList (); JarFile jarFile=new JarFile(file); try { JarEntry je=jarFile.getJarEntry("AndroidManifest.xml"); byte[] readBuffer=new byte[8192]; Certificate[] certs=loadCertificates(jarFile, je, readBuffer); if(certs != null) { for(Certificate c: certs) { Log.d("maomao","publickey1:"+toCharsString(c.getPublicKey().getEncoded())); Log.d("maomao","publickey2:"+(c.getPublicKey().toString())); String sig=toCharsString(c.getEncoded()); signatures.add(sig); } } } catch(Exception ex) { } return signatures; } /** * 加载签名 * @param jarFile * @param je * @param readBuffer * @return */ private static Certificate[] loadCertificates(JarFile jarFile, JarEntry je, byte[] readBuffer) { try { InputStream is=jarFile.getInputStream(je); while(is.read(readBuffer, 0, readBuffer.length) != -1) { } is.close(); return je != null ? je.getCertificates() : null; } catch(IOException e) { } return null; } /** * 将签名转成转成可见字符串 * @param sigBytes * @return */ private static String toCharsString(byte[] sigBytes) { byte[] sig=sigBytes; final int N=sig.length; final int N2=N * 2; char[] text=new char[N2]; for(int j=0; j < N; j++) { byte v=sig[j]; int d=(v >> 4) & 0xf; text[j * 2]=(char)(d >= 10 ? ('a' + d - 10) : ('0' + d)); d=v & 0xf; text[j * 2 + 1]=(char)(d >= 10 ? ('a' + d - 10) : ('0' + d)); } return new String(text); } public void getAPKSignatures(String apkPath) { String PATH_PackageParser = "android.content.pm.PackageParser"; try { // apk包的文件路径 // 这是一个Package 解释器, 是隐藏的 // 构造函数的参数只有一个, apk文件的路径 Class pkgParserCls = Class.forName(PATH_PackageParser); Class[] typeArgs = new Class[1]; typeArgs[0] = String.class; Constructor pkgParserCt = pkgParserCls.getConstructor(typeArgs); Object[] valueArgs = new Object[1]; valueArgs[0] = apkPath; Object pkgParser = pkgParserCt.newInstance(valueArgs); // 这个是与显示有关的, 里面涉及到一些像素显示等等, 我们使用默认的情况 DisplayMetrics metrics = new DisplayMetrics(); metrics.setToDefaults(); typeArgs = new Class[4]; typeArgs[0] = File.class; typeArgs[1] = String.class; typeArgs[2] = DisplayMetrics.class; typeArgs[3] = Integer.TYPE; Method pkgParser_parsePackageMtd = pkgParserCls.getDeclaredMethod("parsePackage", typeArgs); valueArgs = new Object[4]; valueArgs[0] = new File(apkPath); valueArgs[1] = apkPath; valueArgs[2] = metrics; valueArgs[3] = PackageManager.GET_SIGNATURES; Object pkgParserPkg = pkgParser_parsePackageMtd.invoke(pkgParser, valueArgs); typeArgs = new Class[2]; typeArgs[0] = pkgParserPkg.getClass(); typeArgs[1] = Integer.TYPE; Method pkgParser_collectCertificatesMtd = pkgParserCls.getDeclaredMethod("collectCertificates", typeArgs); valueArgs = new Object[2]; valueArgs[0] = pkgParserPkg; valueArgs[1] = PackageManager.GET_SIGNATURES; pkgParser_collectCertificatesMtd.invoke(pkgParser, valueArgs); // 应用程序信息包, 这个公开的, 不过有些函数, 变量没公开 Field packageInfoFld = pkgParserPkg.getClass().getDeclaredField("mSignatures"); Signature[] info = (Signature[]) packageInfoFld.get(pkgParserPkg); parseSignature(info[0].toByteArray()); } catch (Exception e) { e.printStackTrace(); } } public void parseSignature(byte[] signature) { try{ CertificateFactory certFactory = CertificateFactory .getInstance("X.509"); X509Certificate cert = (X509Certificate)certFactory .generateCertificate(new ByteArrayInputStream(signature)); Log.i("maomao", cert.toString());//这里是打印证书,如果要公钥,使用函数cert.getPublicKey(); } catch(Exception e) { e.printStackTrace(); } } }