La documentation de PackageManager.GET_SIGNATURES indique "Cette constante est déconseillée au niveau de l'API 28. Utilisez plutôt GET_SIGNING_CERTIFICATES".
Malheureusement, il n'était pas sécurisé et a été facilement piraté.
Comment pouvez-vous utiliser le nouveau "GET_SIGNING_CERTIFICATES" introduit avec Android P?
Dans API28, vous devez également rechercher plusieurs signataires.
Cette fonction fera le travail(It's in kotlin)
:
fun getApplicationSignature(packageName: String = context.packageName): List<String> {
val signatureList: List<String>
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
// New signature
val sig = context.packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNING_CERTIFICATES).signingInfo
signatureList = if (sig.hasMultipleSigners()) {
// Send all with apkContentsSigners
sig.apkContentsSigners.map {
val digest = MessageDigest.getInstance("SHA")
digest.update(it.toByteArray())
bytesToHex(digest.digest())
}
} else {
// Send one with signingCertificateHistory
sig.signingCertificateHistory.map {
val digest = MessageDigest.getInstance("SHA")
digest.update(it.toByteArray())
bytesToHex(digest.digest())
}
}
} else {
val sig = context.packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES).signatures
signatureList = sig.map {
val digest = MessageDigest.getInstance("SHA")
digest.update(it.toByteArray())
bytesToHex(digest.digest())
}
}
return signatureList
} catch (e: Exception) {
// Handle error
}
return emptyList()
}
Et byteToHex
est:
fun bytesToHex(bytes: ByteArray): String {
val hexArray = charArrayOf('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F')
val hexChars = CharArray(bytes.size * 2)
var v: Int
for (j in bytes.indices) {
v = bytes[j].toInt() and 0xFF
hexChars[j * 2] = hexArray[v.ushr(4)]
hexChars[j * 2 + 1] = hexArray[v and 0x0F]
}
return String(hexChars)
}
Cela gérera signature de l'application in Android 9 (ou inférieur)
Ma solution est:
Dans le jeu de compilation gradle "compileSdkVersion 28" et "targetSdkVersion 28", vous pouvez maintenant utiliser cet exemple de code:
try {
if(Build.VERSION.SDK_INT >= 28) {
@SuppressLint("WrongConstant") final PackageInfo packageInfo = getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_SIGNING_CERTIFICATES);
final Signature[] signatures = packageInfo.signingInfo.getApkContentsSigners();
final MessageDigest md = MessageDigest.getInstance("SHA");
for (Signature signature : signatures) {
md.update(signature.toByteArray());
final String signatureBase64 = new String(Base64.encode(md.digest(), Base64.DEFAULT));
Log.d("Signature Base64", signatureBase64);
}
}
} catch (PackageManager.NameNotFoundException | NoSuchAlgorithmException e) {
e.printStackTrace();
}
Si étrangement Android Studio ne reconnaît pas la constante GET_SIGNING_CERTIFICATES, vous pouvez utiliser l'annotation @SuppressLint ("WrongConstant")).
TL; DR si votre cas d'utilisation est que vous validez les signatures du package appelant, vous pouvez toujours utiliser GET_SIGNATURES dans la pré-api 28 en toute sécurité tant que vous validez tous les signataires renvoyés par le gestionnaire de paquets (au lieu de vous arrêter tôt lorsque vous en trouvez un en qui vous avez confiance). En fait, Google l'a corrigé dans Lollipop ( https://Android.googlesource.com/platform/libcore/+/f8986a989759c43c155ae64f9a3b36f670602521 ).
Détails: Je pense que votre commentaire sur le fait que GET_SIGNATURES soit facilement piraté est basé sur cette vulnérabilité ( https://www.blackhat.com/docs /us-14/materials/us-14-Forristal-Android-FakeID-Vulnerability-Walkthrough.pdf ). Où Android ne valide pas la chaîne de confiance avant de renvoyer les signataires apk.
Ce n'est un problème que si vous avez du code comme celui-ci:
private boolean validateCallingPackage(String: packageName) {
PackageInfo packageInfo;
try {
packageInfo = context.getPackageManager().getPackageInfo(
packageName,
PackageManager.GET_SIGNATURES);
} catch (PackageManager.NameNotFoundException e) {
return false;
}
for (Signature signature : packageInfo.signatures) {
String hashedSignature = Utility.sha256hash(signature.toByteArray());
if (validAppSignatureHashes.contains(hashedSignature)) {
return true; //THIS is the problematic code
}
}
return false
}
Le code retourne vrai s'il trouve un certificat qui correspond à un de votre liste blanche. Avec la vulnérabilité Android, si les signatures contenaient une signature d'un signataire malveillant, votre code retourne toujours vrai.
L'atténuation de cette vulnérabilité consiste à vérifier à la place TOUTES les signatures renvoyées par le gestionnaire de packages et à retourner false si aucune d'entre elles ne figure dans votre liste blanche. c'est à dire.
private boolean validateCallingPackage(String: packageName) {
...
for (Signature signature : packageInfo.signatures) {
String hashedSignature = Utility.sha256hash(signature.toByteArray());
if (!validAppSignatureHashes.contains(hashedSignature)) {
return false; //FIXED
}
}
return true
}