How to calculate SHA-1 fingerprint of an X.509 certificate in Flutter?

193 views Asked by At

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.

1

There are 1 answers

1
Debabrata Samal On BEST ANSWER

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