Physical Address:
ChongQing,China.
WebSite:
在某次调试过程中,我发现ADB安装APK出现了如下报错:INSTALL_PARSE_FAILED_NO_CERTIFICATES。
这是我第一次遇到这个问题,虽然长期从事安卓开发,但都集中在Framework、HAL与内核,对于App的开发了解有限,完全算不上专业的App开发者。而且在此之前,调试时多次安装我都没有遇到这种问题。经过排查,发现该问题出现的原因是因为该APK为Release版本,而安卓系统针对Release版本的APK是要求应用必须进行签名的,而debug版本的签名则不需要。
那么如何对安卓的APK进行签名以及安卓系统是如何进行校验的呢。这里我们将两种方式来对APK进行签名。
方式一,我们通过命令行方式来进行签名:
首先我们需要创建Java KeyStore文件,也就是我们在很多Android工程中见到的*.jks文件,这里我们通过keytool命令来进行创建:
keytool -genkeypair \
-alias ${JKS_ALIAS} \
-keyalg RSA \
-keysize 2048 \
-validity 365 \
-keystore "${PATH_TO_JKS} \
-storepass "${KEYSTORE_PASSWORD}" \
-keypass "${KEY_PASSWORD}" \
-dname "CN=WH, OU=WH, O=WH, L=China, ST=China, C=China"
这里面各个参数的含义:
alias:用于对该JKS设置别名;
keyalg:证书加密算法;
keysize:证书加密算法位数;
validity:证书有效期;
keystore:签名证书文件;
storepass:KeyStore的密码;
keypass:证书私钥密码;
dname:证书签名的身份信息;
之后我们可以检测是否正常生成JKS文件:
keytool -list -v -keystore ${PATH_TO_JKS}
之后我们再进行签名处理:
jarsigner -verbose -keystore "${PATH_TO_JKS}" \
-storepass "$KEYSTORE_PASSWORD" \
-keypass "$KEY_PASSWORD" \
${NEED_SIGNED_APK} "$JKS_ALIAS" \
-signedjar "${SIGNED_APK}"
最后我们可以检查是否完成了签名:
jarsigner -verify -verbose ${SIGNED_APK}
#其输出大概是这样的
s = signature was verified
m = entry is listed in manifest
k = at least one certificate was found in keystore
- Signed by "CN=WH, OU=WH, O=WH, L=China, ST=China, C=China"
Digest algorithm: SHA-256
Signature algorithm: SHA256withRSA, 2048-bit key
jar verified.
Warning:
This jar contains entries whose certificate chain is invalid. Reason: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
This jar contains entries whose signer certificate is self-signed.
This jar contains signatures that do not include a timestamp. Without a timestamp, users may not be able to validate this jar after any of the signer certificates expire (as early as 2026-02-21).
POSIX file permission and/or symlink attributes detected. These attributes are ignored when signing and are not protected by the signature.
Re-run with the -verbose and -certs options for more details.
此时我们就已经完成了对APK的签名。
方式二,我们通过gradle配置来实现签名,此时我们一般通过AndroidStudio来操作。
首先我们通过AndroidStudio,点击上方Build-Generate Signed Bundle/APK,选择aab或者APK,之后我们可以看到在Key store path看到create new的按钮,点击就可以创建jks文件:
具体填入的信息与我们手动创建该文件其实是一样的,只不过交互形式改变了而已。
在生成完jks文件后,我们需要在build.gradle中添加如下配置:
signingConfigs {
release {
keyAlias 'your-key-alias'
keyPassword 'your-key-password'
storeFile file('/path/to/your/keystore/file.jks')
storePassword 'your-keystore-password'
}
}
这样当我们在AndroidStudio中编译生成APK时,就会自动进行签名。
当我们拿到一个APK时,在不确定其是否为Release或者Debug的前提下,我们可以通过AndroidStudio进行打开,找到应用的AndroidManifest.xml,找到debuggable字段:
如果android:debuggable=”true”,则表明该apk为debug版本的apk,否则则为release版本的apk。
关于签名还有一点需要说明的是,如果我们在签名之后更换了签名文件,编译出来的产物在安装时可能会出现unmatched signature,这是因为之前的签名与后续的签名文件存在差异,这在Android系统中是不被接受的。
最后,我们需要理解为什么需要对我们的应用进行签名。其实很简单,对应用签名其实是为了表明应用的所有者的身份信息,尤其是我们上架到应用市场时,我们后续应用的更新和升级都会进行签名的验证。如果某个第三方在不知情或未经授权的情况下设法取得我们的应用签名密钥,此人可能会为应签名并进行分发,从而恶意替换我们的原版应用。另外,还可能会利用开发者的身份为应用签名并进行分发,从而攻击其他应用或系统本身,或者损坏或窃取用户数据。