본문 바로가기
mobile/flutter

[Flutter | AOS] 위변조 탐지 - 서명값 비교

by moonsiri 2021. 10. 1.
728x90
반응형

keytool 명령어를 사용하여 key store에서 해시값을 추출합니다.

해당 해시값을 프로퍼티 파일에 저장해둡니다.

 

저장한 해시값과 현재 App의 해시값을 비교합니다.

class MainActivity: FlutterActivity() {

    /**
     * onCreate
     */
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        if (!verifyAppSignature()) {
            super.finish()
        }
    }

    fun verifyAppSignature(): Boolean {
        val currentSignature: List<String> = getApplicationSignature();
        if (currentSignature.size > 0) {
            val approvedSignatures: MutableList<String> = ArrayList<String>()
            approvedSignatures.add("프로퍼티에서 조회한 해시값")

            //first checking if no unapproved signatures exist
            for (signatureHex in currentSignature) {
                if (!approvedSignatures.contains(signatureHex)) {
                    return false
                }
            }
            //now checking if any of approved signatures exist
            for (signatureHex in currentSignature) {
                if (approvedSignatures.contains(signatureHex)) {
                    return true
                }
            }
        }
        return false
    }

    fun getApplicationSignature(): List<String> {
        val context: Context = getApplicationContext()
        val pm: PackageManager = context.getPackageManager()
        val packageName: String = context.getPackageName()
        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("SHA1")
                        digest.update(it.toByteArray())
                        bytesToHex(digest.digest())
                    }
                } else {
                    // Send one with signingCertificateHistory
                    sig.signingCertificateHistory.map {
                        val digest = MessageDigest.getInstance("SHA1")
                        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("SHA1")
                    digest.update(it.toByteArray())
                    bytesToHex(digest.digest())
                }
            }

            return signatureList
        } catch (e: Exception) {
        }

        return emptyList()
    }

    fun bytesToHex(bytes: ByteArray): String {
        val hexArray = "0123456789ABCDEF".toCharArray()
        val hexChars = CharArray((bytes.size * 3) - 1)
        var v: Int

        for(j in bytes.indices){
            v = bytes[j].toInt() and 0xFF
            hexChars[j * 3] = hexArray[v ushr 4]
            hexChars[j * 3 + 1] = hexArray[v and 0x0F]
            if(j < bytes.lastIndex){
                hexChars[j * 3 + 2] = ':'
            }
        }
        return String(hexChars)
    }
}

 

 

[Reference]

https://stackoverflow.com/questions/52041805/how-to-use-packageinfo-get-signing-certificates-in-api-28?answertab=votes#tab-top 

https://yujuwon.tistory.com/entry/keystore-%ED%95%B4%EC%8B%9C-%EA%B0%92-%ED%99%95%EC%9D%B8%ED%95%98%EA%B8%B0

728x90
반응형

댓글