I am trying to fix this issue Improper Verification of App Signature at Runtime. for my flutter app.
Description of this issue is
Android apps are digitally signed. A digital signature, in this context, is a cryptographic construct that a developer applies to a piece of software to prove he/she wrote it. If an attacker has modified something in the app and re-sign it with its own signature, the app should not be able to run. The application should check the current signature of the app with the developer’s signature at runtime.
So I have implemented the following code in my app
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:package_info_plus/package_info_plus.dart';
Future<void> verifyAppSignature() async {
if (kDebugMode) {
print('Skipping signature verification in debug mode.');
return;
}
if (kReleaseMode) {
PackageInfo packageInfo = await PackageInfo.fromPlatform();
String appSignature = await getPackageSignature(packageInfo.packageName);
String developerSignature = 'my_sha-1_key';
// storing the sha key in code is not recommended
if (appSignature != developerSignature) {
print("appSign: $appSignature");
// SystemNavigator.pop();
// throw Exception('Release signature verification failed');
}
}
}
Future<String> getPackageSignature(String packageName) async {
try {
const MethodChannel channel = MethodChannel('app-release');
final String signature = await channel.invokeMethod('getSignature');
return signature;
} on PlatformException catch (e) {
return e.toString();
}
}
Changes made to MainActivity.kt
package com.example.appname
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.os.Build
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugins.GeneratedPluginRegistrant
class MainActivity: FlutterActivity() {
private val CHANNEL = "app-release"
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
GeneratedPluginRegistrant.registerWith(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
if (call.method == "getSignature") {
val signature = getSignature()
result.success(signature)
} else {
result.notImplemented()
}
}
}
private fun getSignature(): String {
return try {
val packageInfo: PackageInfo = packageManager.getPackageInfo(
packageName,
PackageManager.GET_SIGNATURES
)
val signature = packageInfo.signatures?.getOrNull(0)
signature?.let {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
it.toCharsString()
} else {
it.toString()
}
} ?: ""
} catch (e: Exception) {
e.printStackTrace()
""
}
}
}
But The problem here the appSignature
returns a X.509 certificate
fingerprint/hash and my developerSignature
is a sha-1
fingerprint.
If there is a method to calculate the sha-1 fingerprint
of X.509 certificate
then it would be a big help.
I have figured it out.
Inside MainActivity.kt
I calculated the sha-1 fingerprint
of the X.509 Certificate
.
imports
import java.io.ByteArrayInputStream
import java.security.cert.CertificateFactory
import java.security.cert.X509Certificate
then inside getSignature()
signature?.let {
val certFactory = CertificateFactory.getInstance("X.509")
val x509Certificate = certFactory.generateCertificate(ByteArrayInputStream(it.toByteArray())) as X509Certificate
val sha1Bytes = x509Certificate.encoded.sha1()
// Convert the byte array to a hex string
sha1Bytes.joinToString("") { "%02x".format(it) }
} ?: ""
Kotlin extension function to compute the SHA-1 hash of a ByteArray.
private fun ByteArray.sha1(): ByteArray {
val digest = java.security.MessageDigest.getInstance("SHA-1")
return digest.digest(this)
}
You can replace "SHA-1" with "SHA-256" or other algorithms if needed