1

目前在我的 android 应用程序中,由 DexGuard 保护以混淆字符串和敏感信息,如源代码中存在的网络 API 密钥。
我使用了 DexGuarsd,因此没有人可以对其进行逆向工程。

但是我需要停止 DexGuard 订阅。
因此,如果我使用 Android Jetpack Security EncryptedSharedPreferencesEncryptedFile.Builder它会帮助我实现像 DexGaurd 提供的那样的安全性。

例如:如果我使用EncryptedSharedPreferences下面的代码,那么如果有人可以访问我的源代码 OR .apk,那么他/她是否能够看到敏感值PASSWORD-XXXXXXXX
因为上面的密码存在于我的源代码文件中。

encryptedSharedPreferences.edit().apply {
 putString("MY_KEY","PASSWORD-XXXXXXXX"
 }.apply()

我的目标是确保即使有人获得了我的源代码,上述值“PASSWORD-XXXXXXXX”也永远不会被任何人解密或看到。

我需要你的指导来实现它。请指点前进的方向。

4

2 回答 2

2

不管你做什么,如果有人想把他们的手放在存储在源代码中的字符串上,你就无法阻止它们

暴露网络 api 很好,因为这是各种即时通讯应用程序中的常见做法,即使您可以以某种方式使 api 从源代码中无法读取,也可以使用适当的工具简单地嗅探网络流量,从而使所有保护功能无用

存储密码是完全错误的,存储运行时生成的哈希,而不是在这里和那里放一些盐和胡椒(让它变得辣)

使用 ssl/tls 使服务器客户端对话难以阅读

请记住,一把锁只能将诚实的人拒之门外,放一把,但永远不要依赖它

于 2021-10-10T10:26:08.757 回答
-4

简单地将您的密码保存在应用级build.graldle文件中,例如:

android {
    compileSdkVersion 30
    buildToolsVersion = '29.0.3'
    defaultConfig {
        buildConfigField 'String', 'prefPass', '"YourPasswordHere"'
        // rest of the code goes here..

并像这样使用它:

BuildConfig.prefPass

由于build.gradle文件不是 APK 源代码的一部分,因此没有人可以窃取它。

为什么要将密码保存在共享首选项中?为什么不能直接从 BuildConfig 使用它?如果您真的想保存它,请使用此类加密并保存它:

import android.util.Log
import java.security.MessageDigest
import java.util.*
import javax.crypto.Cipher
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec

object CryptographyUtil {

    private const val TAG: String = "CryptographyUtil"
    private const val CIPHER_TRANSFORMATION = "AES/CBC/PKCS5PADDING"
    private const val KEY_ALGORITHM = "AES"

    // https://developer.android.com/guide/topics/security/cryptography#encrypt-message
    fun encryptData(key: String, data: String): String {
        try {
            val secretKeySpec = generateSecretKey(key)
            val cipher = Cipher.getInstance(CIPHER_TRANSFORMATION)
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, getIVSpecification(key))
            val encryptValue = cipher.doFinal(data.toByteArray())
            return if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
                Base64.getEncoder().encodeToString(encryptValue)
            } else {
                android.util.Base64.encodeToString(encryptValue, android.util.Base64.DEFAULT)
            }
        } catch (e: Exception) {
            Log.d(TAG, "Error while encrypt ${e.message}")
        }
        return data
    }

    fun decryptData(key: String, encryptedData: String): String {
        try {
            val secretKeySpec = generateSecretKey(key)
            val cipher = Cipher.getInstance(CIPHER_TRANSFORMATION)
            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, getIVSpecification(key))
            val decodeValue = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
                Base64.getDecoder().decode(encryptedData)
            } else {
                android.util.Base64.decode(encryptedData, android.util.Base64.DEFAULT)
            }
            return String(cipher.doFinal(decodeValue))
        } catch (e: Exception) {
            Log.d(TAG, "Error while decrypt ${e.message}")
        }
        return encryptedData
    }

    private fun generateSecretKey(key: String): SecretKeySpec {
        val messageDigest = MessageDigest.getInstance("SHA-256")
        val bytes = key.toByteArray(Charsets.UTF_8)
        messageDigest.update(bytes, 0, bytes.size)
        return SecretKeySpec(messageDigest.digest(), KEY_ALGORITHM)
    }

    private fun getIVSpecification(key: String): IvParameterSpec {
        // concat string so that key has always size greater than 16 bytes & we can
        // get first 16 character for generating IV specification.
        // As documentation suggest that IV specification key can't be less or greater than 16 bytes
        val concatKey = key + key
        return IvParameterSpec(concatKey.substring(0, 16).toByteArray())
    }
}

在您的课程中使用上述EncryptedSharedPreferences课程:

val encryptData = encryptData(BuildConfig.prefPass, 
BuildConfig.prefPass)
encryptedSharedPreferences.edit().apply {
    putString("MY_KEY", encryptData)
 }.apply()

注意:要完成这项工作,您需要启用 minify 以隐藏BuildConfig文件。

buildTypes {
        release {
            debuggable false
            minifyEnabled true
            shrinkResources true
            zipAlignEnabled true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
        debug {
                minifyEnabled true
                shrinkResources true
                zipAlignEnabled true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

在这里检查这个答案

缩小步骤的另一个特点是常量的内联。这可以解释为什么 BuildConfig 消失了,但值仍然存在于需要的地方。一旦值被内联,就不再有对 BuildConfig 类的引用,并且 minifier 可以完全删除它

加密部分只是为了防止 Key 从应用程序中被读取,根据定义,这是不可能的。如果您的应用程序可以使用密钥,那么您的应用程序的修改版本可以使用它,该版本将密钥转储到 LogCat 或其他东西。ProGuard、DexGuard 和 kin 等工具使访问变得更加困难,但它们无法阻止它。防止密钥被访问的唯一方法是首先不在应用程序中拥有它。参考:https ://stackoverflow.com/a/30238695/2462531

于 2021-10-07T06:03:03.033 回答