最近有需求是需要获取未安装的apk和已经安装的app的签名信息。记录一下代码。
1、命令读取签名信息
keytool -printcert -jarfile xxx.apk
测试如下:
2、代码获取签名信息
测试如下:
可以看到命令读取的签名信息和代码获取的是一样的,证明代码获取的没有问题。
apk MD5:7815c5174154b7ce33f023602106ecc6 是获取的apk的签名信息
app MD5:7815c5174154b7ce33f023602106ecc6 是获取的app的签名信息
代码如下:
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.cert.Certificate;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class SignUtil {
/**
* 从 apk 中获取 MD5 签名信息
*
* @param apkPath
* @return
* @throws Exception
*/
public static String getApkSignatureMD5(String apkPath) throws Exception {
byte[] bytes = SignUtil.getSignaturesFromApk(apkPath);
String sign = hexDigest(bytes, "MD5");
return sign;
}
public static String getApkSignatureSHA1(String apkPath) throws Exception {
byte[] bytes = getSignaturesFromApk(apkPath);
String sign = hexDigest(bytes, "SHA1");
return sign;
}
public static String getApkSignatureSHA256(String apkPath) throws Exception {
byte[] bytes = getSignaturesFromApk(apkPath);
String sign = hexDigest(bytes, "SHA256");
return sign;
}
/**
* 获取已经安装的 app 的 MD5 签名信息
*
* @param context
* @param pkgName
* @return
*/
public static String getAppSignatureMD5(Context context, String pkgName) {
return getAppSignature(context, pkgName, "MD5");
}
public static String getAppSignatureSHA1(Context context, String pkgName) {
return getAppSignature(context, pkgName, "SHA1");
}
public static String getAppSignatureSHA256(Context context, String pkgName) {
return getAppSignature(context, pkgName, "SHA256");
}
public static String getAppSignature(Context context, String pkgName, String algorithm) {
try {
PackageInfo packageInfo = context.getPackageManager().getPackageInfo(
pkgName, PackageManager.GET_SIGNATURES);
Signature[] signs = packageInfo.signatures;
Signature sign = signs[0];
String signStr = hexDigest(sign.toByteArray(), algorithm);
return signStr;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return "";
}
/**
* 从APK中读取签名
*
* @param apkPath
* @return
* @throws IOException
*/
public static byte[] getSignaturesFromApk(String apkPath) throws IOException {
File file = new File(apkPath);
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) {
return c.getEncoded();
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
/**
* 加载签名
*
* @param jarFile
* @param je
* @param readBuffer
* @return
*/
public 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) {
e.printStackTrace();
}
return null;
}
public static String hexDigest(byte[] bytes, String algorithm) {
MessageDigest md5;
try {
md5 = MessageDigest.getInstance(algorithm);
} catch (Exception e) {
e.printStackTrace();
return "";
}
byte[] md5Bytes = md5.digest(bytes);
StringBuffer hexValue = new StringBuffer();
for (int i = 0; i < md5Bytes.length; i++) {
int val = ((int) md5Bytes[i]) & 0xff;
if (val < 16) {
hexValue.append("0");
}
hexValue.append(Integer.toHexString(val));
}
return hexValue.toString();
}
}