【问题标题】:How to protect Google Play public key when doing InApp Billing进行 InApp 计费时如何保护 Google Play 公钥
【发布时间】:2016-09-21 22:43:42
【问题描述】:

其实这对保护公钥有点傻(那么公钥的定义是什么?)但是根据documentation by Google

为了保护您的公钥免受恶意用户和黑客的攻击,请不要 将其作为文字字符串嵌入任何代码中。相反,构建 在运行时从片段中提取字符串或使用位操作(例如, XOR 与其他字符串)以隐藏实际密钥。关键本身是 不是秘密信息,但你不想让它变得容易 黑客或恶意用户用另一个密钥替换公钥。

有什么推荐的方法吗?

我知道有很多方法可以做到这一点,我只是不想遵循人们过去处理密码散列的方式(例如 md5、sha1 等),我想了解上述用例中的最佳实践。

【问题讨论】:

    标签: java android google-play


    【解决方案1】:

    这在这里出现了很多 :) 您引用的段落背后的想法是,为了确保应用内结算安全,您需要验证交易签名。这些是使用与您的开发者帐户相关联的私钥签名的。密钥位于 Google 的服务器上,因此可以相当安全地假设没有其他人可以用它签署数据。要验证它,您需要您的公钥,您可以从开发者控制台复制它。如果有人在您的应用中替换了它,他们可能会欺骗它接受来自未经授权来源的应用内计费交易,因为如果他们植入公钥,他们可能还会控制相应的私钥。然而,在实践中,只需在正确的位置修改代码以始终为 isLicensed()hasItem() 或您可能拥有的类似方法返回 true 而没有人这样做会容易得多。

    当然,保护密钥的最佳方法是在您的应用中完全不使用密钥。将所有事务验证逻辑移至您的服务器,并使用 HTTPS 连接到它。正确验证证书链以确保您正在与自己的服务器通信。否则,有人可能会弄乱 DNS 并欺骗您的应用程序连接到他们自己的服务器。几周前宣布了针对 iOS 购买的类似攻击。

    接下来最好的办法是以某种方式混淆密钥,并将其包含在您的应用中。这样做的好处是您不需要服务器,但缺点是如果有人足够坚定,他们会弄清楚的,因为他们总是可以反转您的应用程序的字节码。所以你最好的办法是想出你自己的原始方法来做这件事,不会出现在公共论坛上:) 为了让它更难一点,你可以在本机代码中实现验证部分,这更难(但不是不可能)分析。尽管如此,如上所述,在正确的位置修补字节码比尝试替换公钥要容易得多,因此大多数破解者都会这样做。

    【讨论】:

    • 你的意思是“然而,在实践中,简单地修改你的代码在正确的地方总是更容易为 isLicensed()、hasItem() 或你可能拥有的类似方法返回 true 而没有一个人这样做。”?这句话没有完全的意义。请澄清。
    • 这意味着更容易修补应用程序并彻底禁用您可能拥有的任何许可检查,而不是尝试替换密钥并尝试欺骗签名验证。
    • ~"可能也控制了对应的私钥"。但应用内结算请求通过设备上的 Play 商店 APK。那么有人将如何控制或操纵该逻辑??
    • @IgorGanapolsky 当他们构建一个新的修改后的 apk 时,他们可以在不去 Play 商店的情况下模拟购买(类似于 luckypatcher),因此,他们可以向您的应用程序响应一个有效的购买字符串,其签名对应于修改后的公钥。
    【解决方案2】:

    至少做简单的文本转换。这个想法是普通的 dex 反汇编不会泄露你的公钥。

    下面是简单的字符串编码/解码的函数示例:

    /**
     * Simple String transformation by XOR-ing all characters by value.
     */
    static String stringTransform(String s, int i) {
       char[] chars = s.toCharArray();
       for(int j = 0; j<chars.length; j++)
          chars[j] = (char)(chars[j] ^ i);
       return String.valueOf(chars);
    }
    

    然后您的私钥作为编码字符串存储在源中(使用此函数对其进行编码),并在运行时使用相同的函数进行解码。这是谷歌推荐的一种“异或”方法。

    你自己做'i'参数,任何随机的,如0x27或其他都可以。 如果您以这种方式隐藏更多字符串,请为每个转换使用不同的“i”。

    【讨论】:

    • 神秘 :) 如果你是程序员,你肯定能克服这样的问题。
    • @NPike,您需要转义字符串才能在 Java 中使用它。试试这个网站:htmlescape.net/javaescape_tool.html 不幸的是,我发现转换过程通常会导致 Eclipse 无法处理的字符串(即使转义),所以你必须调整 XORed 的值,直到你能得到一些东西使用...
    【解决方案3】:

    作为手动混淆密钥的替代方法,您还可以让混淆器自动完成。 ProGuard 是 Android SDK 的一部分,但它主要混淆类/字段/方法名称,而不是字符串。它专门用于 Android 的兄弟DexGuard 可以添加更多的混淆层,应用字符串加密、类加密和反射。它不是免费的,但它可以节省时间,而且可能比手动操作更有效。

    (我是 ProGuard 和 DexGuard 的开发者)

    【讨论】:

    • 仅仅混淆一个简单的字符串,DexGuard 不是矫枉过正吗?我的意思是,有什么保证它会做对呢?
    【解决方案4】:

    将您的公钥存储在服务器端,一旦您从 google play 获得响应以验证密钥,将该响应发送到服务器并在服务器上执行您的操作。

    【讨论】:

    • 那假设已经设置了这样的服务器端基础设施。
    【解决方案5】:

    公钥是 base64 编码的 ([a-zA-Z0-9+/]),因此您可以轻松避免转义混淆字符串的需要,因为这是 @PointerNull 解决方案中的一个烦人问题。

    相反,您可以通过首先将相关字符转换为 6 位 int 来执行混淆。然后进行位操作(例如 XOR-ing),然后转换回 base64 编码的字符。保证不需要字符转义。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-06-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多