Android应用安全机制
2016-11-09 23:04
281 查看
目录:
Android应用包名的作用
Android应用签名原理
Android应用签名过程
Android应用在系统中的运行机制
Android应用在系统中会被分配唯一的UID,用来标识应用在系统中的唯一身份。每一个UID都对应着相应的进程,系统分配UID是根据应用的包名和AndroidManifest.xml文件中manifest节点的android:sharedUserId=""属性确定的。正常情况下,如果没有特别说明sharedUserId属性,系统会为每一个包名映射一个UID,也就是说每一个应用都专属一个独立的进程。但是,特殊情况下,如果两个应用具有相同的sharedUserId属性,且使用相同的证书签名(包名可以不同),那么系统会将两个应用分配同一个UID,使得两个应用运行在同一个进程当中。Android应用包名的作用
应用包名相当于人的身份证,是应用的唯一标识(eclipse中包名作为应用唯一标识,studio中使用applicationId作为应用唯一标识,但是getPackageName()方法返回的是applicationId)。设想一下,如果同一设备系统当中出现多个相同包名的应用,犹如多个人具有相同的身份证。如果某个人犯事了,要依法追究责任的话,根据身份证来找的话可能会找到多个人,那社会可能就会混乱了!对于系统来说亦是如此,如果没有唯一标识应用身份的东西的话,系统管理也会很困难。所以同一设备系统当中,不能存在相同包名的应用。包名既然是唯一的,一般采用“反域名命名”方式定义包名。也就是说如果我公司的地址是:www.everyoo.com,那么反域名后就是:com.everyoo.***。因为域名都是唯一的,所以反域名后也可以保证包名的唯一性。
Android应用签名原理
在梳理签名原理之前,要先明确几个概念:消息摘要:使用单向的hash函数对消息进行处理,结果会生成一个固定长度的、唯一映射源消息的摘要;如果消息(甚至一个字节)发生了改变,接受者通过比较从接收的消息解析的摘要和原摘要,便可判断消息是否被改变,常被用作数字签名。
私钥:用来对数据进行加密,和公钥成对出现。
公钥:用来验证数据的合法性,和私钥成对出现。
应用包名在设备上都是可看到的,所以存在一种情况:第三方创建相同包名的应用,然后覆盖我的应用。为了确保应用作者的正确性及应用的完整性,所以需要对应用进行签名。
签名的原理:
1.使用hash函数对应用生成消息摘要
2.使用签名文件对消息摘要进行签名
3.接受者使用公钥获取消息摘要,然后使用相同的hash函数处理消息体,获取消息摘要,比较两个消息摘要是否相同。
签名的方式:
1.signjar:signjar是jdk自带的,在jdk的bin目录下,使用.keystore(eclipse中)或.jks(studio中)文件对应用签名;
2.[b]signapk:[b]signapk是后续为android应用开发的,存在于android源码中,使用的是.x509.pem和.pk8文件签名。且.keystore/.jks文件可以和.pem和.pk8文件相互转换;[/b][/b]
[b][b] 签名文件的生成(手动):[/b][/b]
[b][b]
[/b][/b]
[b][b] -genkey:生成秘钥[/b][/b]
[b][b] -alias:签名文件别名[/b][/b]
[b][b] -keyalg:签名算法[/b][/b]
[b][b] -keysize:算法加密后秘钥长度[/b][/b]
[b][b] -validity:签名文件有效时间[/b][/b]
[b][b] -demo.jks:签名文件名称(也可以是.keystore格式,这个无关紧要)[/b][/b]
[b][b] demo.jks的信息如下:
[/b]
[/b]
签名命令:
signjar:jarsigner -verbose -keystore demo.jks -signedjar app-debug-signed.apk app-debug.apk demo
demo.jks:签名文件
demo:签名文件别名
app-debug-signed.apk:签名后的apk文件
app-debug.apk:签名前的apk文件
signapk:java -jar signapk platform.x509.pem platform.pk8 app-debug.apk app-debug-signed.apk
.x509.pem:包含公钥的文件
.pk8:私钥文件
两种签名方式的区别:jarsigned使用keystore签名,signapk使用x509.pem和pk8文件 进行签名,其实,keystore文件是可以和x509.pem及pk8文件相互转换的,具体详情请自行搜索。
Android应用签名过程
应用签名后,修改apk文件的扩展名为rar或zip,解压后有个META-INF目录中包含三类文件CERT.RAS/DEMO.RSA
CERT.SF/DEMO.SF
MANIFEST.MF
1.MANIFEST.MF文件
内容如下:
看起来像是文件的SHA1摘要,源码如下:
// MANIFEST.MF Manifest manifest = addDigestsToManifest(inputJar); je = new JarEntry(JarFile.MANIFEST_NAME); je.setTime(timestamp); outputJar.putNextEntry(je); manifest.write(outputJar);
/** Add the SHA1 of every file to the manifest, creating it if necessary. */ private static Manifest addDigestsToManifest(JarFile jar) throws IOException, GeneralSecurityException { Manifest input = jar.getManifest(); Manifest output = new Manifest(); Attributes main = output.getMainAttributes(); if (input != null) { main.putAll(input.getMainAttributes()); } else { main.putValue("Manifest-Version", "1.0"); main.putValue("Created-By", "1.0 (Android SignApk)"); } BASE64Encoder base64 = new BASE64Encoder(); MessageDigest md = MessageDigest.getInstance("SHA1"); byte[] buffer = new byte[4096]; int num; // We sort the input entries by name, and add them to the // output manifest in sorted order. We expect that the output // map will be deterministic. TreeMap<String, JarEntry> byName = new TreeMap<String, JarEntry>(); for (Enumeration<JarEntry> e = jar.entries(); e.hasMoreElements(); ) { JarEntry entry = e.nextElement(); byName.put(entry.getName(), entry); } for (JarEntry entry: byName.values()) { String name = entry.getName(); if (!entry.isDirectory() && !name.equals(JarFile.MANIFEST_NAME) && !name.equals(CERT_SF_NAME) && !name.equals(CERT_RSA_NAME) && (stripPattern == null || !stripPattern.matcher(name).matches())) { InputStream data = jar.getInputStream(entry); while ((num = data.read(buffer)) > 0) { md.update(buffer, 0, num); } Attributes attr = null; if (input != null) attr = input.getAttributes(name); attr = attr != null ? new Attributes(attr) : new Attributes(); attr.putValue("SHA1-Digest", base64.encode(md.digest())); output.getEntries().put(name, attr); } } return output; }
可以看到,manifest集成了所有文件的SHA-1处理后base64转换后的摘要。我们可以做个测试(先下载hashtab
):
查看AndroidManifest.xml文件对应的hash值
取出SHA-1对应的哈希值“C9FC2C9CE58BFC335417164A0B6496F444DE0D16”,然后通过hex - base64在线转码:
可以看到转换后的数据是和MANIFEST.MF中AndroidManifest.xml的SHA1-Digest相同。
2.CERT.SF
文件内容如下:内容与MANIFEST.MF文件中类似,我们再看源码:
// CERT.SF
Signature signature = Signature.getInstance("SHA1withRSA");
signature.initSign(privateKey);
je = new JarEntry(CERT_SF_NAME);
je.setTime(timestamp);
outputJar.putNextEntry(je);
writeSignatureFile(manifest,
new SignatureOutputStream(outputJar, signature));
/** Write a .SF file with a digest of the specified manifest. */
private static void writeSignatureFile(Manifest manifest, SignatureOutputStream out)
throws IOException, GeneralSecurityException {
Manifest sf = new Manifest();
Attributes main = sf.getMainAttributes();
main.putValue("Signature-Version", "1.0");
main.putValue("Created-By", "1.0 (Android SignApk)");
BASE64Encoder base64 = new BASE64Encoder();
MessageDigest md = MessageDigest.getInstance("SHA1");
PrintStream print = new PrintStream(
new DigestOutputStream(new ByteArrayOutputStream(), md),
true, "UTF-8");
// Digest of the entire manifest
manifest.write(print);
print.flush();
main.putValue("SHA1-Digest-Manifest", base64.encode(md.digest()));
Map<String, Attributes> entries = manifest.getEntries();
for (Map.Entry<String, Attributes> entry : entries.entrySet()) {
// Digest of the manifest stanza for this entry.
print.print("Name: " + entry.getKey() + "\r\n");
for (Map.Entry<Object, Object> att : entry.getValue().entrySet()) {
print.print(att.getKey() + ": " + att.getValue() + "\r\n");
}
print.print("\r\n");
print.flush();
Attributes sfAttr = new Attributes();
sfAttr.putValue("SHA1-Digest", base64.encode(md.digest()));
sf.getEntries().put(entry.getKey(), sfAttr);
}
<span style="white-space:pre"> </span>//签名信息在上面并没有使用的到
sf.write(out);
// A bug in the java.util.jar implementation of Android platforms
// up to version 1.6 will cause a spurious IOException to be thrown
// if the length of the signature file is a multiple of 1024 bytes.
// As a workaround, add an extra CRLF in this case.
if ((out.size() % 1024) == 0) {
out.write('\r');
out.write('\n');
}
}
代码中的manifest文件即为MANIFEST.MF文件,对MANIFEST.MF文件的每一项都进行SHA-1摘要,其中SHA1-Digest-Manifest对应的就是MANIFEST.MF文件的base64码。
MANIFEST.MF文件对应的hash值是:
我们把hash值转成base64结果是:
我们可以看到输出的base64码和CERT.SF文件中SHA1-Digest-Manifest的值是相同的,从而印证了我们的猜测;
3.CERT.RSA
代码如下:
// CERT.RSA
je = new JarEntry(CERT_RSA_NAME);
je.setTime(timestamp);
outputJar.putNextEntry(je);
writeSignatureBlock(signature, publicKey, outputJar);
/** Write a .RSA file with a digital signature. */ private static void writeSignatureBlock( Signature signature, X509Certificate publicKey, OutputStream out) throws IOException, GeneralSecurityException { SignerInfo signerInfo = new SignerInfo( new X500Name(publicKey.getIssuerX500Principal().getName()), publicKey.getSerialNumber(), AlgorithmId.get("SHA1"), AlgorithmId.get("RSA"), signature.sign());
PKCS7 pkcs7 = new PKCS7( new AlgorithmId[] { AlgorithmId.get("SHA1") }, new ContentInfo(ContentInfo.DATA_OID, null), new X509Certificate[] { publicKey }, new SignerInfo[] { signerInfo });
pkcs7.encodeSignedData(out); }
这个文件中保存了CERT.SF文件的签名和公钥。参考文章:android签名机制
Android签名机制之---签名过程详解Android签名与认证详细分析之一(CERT.RSA剖析)重新打包apk,使用java bin目录里的jarsigner进行签名
相关文章推荐
- android应用安全——签名机制
- android应用安全——签名机制
- android应用安全——签名机制
- Android 安全机制
- Android应用安全与校验之反动态调试
- 从NDK在非Root手机上的调试原理探讨Android的安全机制
- Android中实现应用切换主题机制
- Android广播事件机制及应用(实现简单的定时提醒功能)
- Android 安全机制
- SEAndroid安全机制简要介绍和学习计划
- 【Android】BroadCast广播机制应用与实例
- Android应用安全防御
- Android permission权限与安全机制解析(下)PACKAGE_USAGE_STATS以及悬浮窗
- Android 应用安全开发之浅谈加密算法
- android permission权限与安全机制解析(下)
- SeAndroid 安全机制研究学习心得
- SEAndroid安全机制中的进程安全上下文关联分析
- Android的Handler Looper Message机制应用实例与详解(二) 推荐
- 从NDK在非Root手机上的调试原理探讨Android的安全机制