Android 计费 - 在 Security.java 文件中,base64EncodedPublicKey 应该是编码值吗?

Posted

技术标签:

【中文标题】Android 计费 - 在 Security.java 文件中,base64EncodedPublicKey 应该是编码值吗?【英文标题】:Android billing - in the file Security.java should the base64EncodedPublicKey be the encoded value? 【发布时间】:2012-07-11 17:36:36 【问题描述】:

我应该将我的应用程序的实际公钥粘贴到这个变量的值中吗?

或者我应该对其进行编码,然后无论编码的字符串是什么,我都会将该字符串变成这个变量的值?

应该是哪个?

【问题讨论】:

你的应用有服务器组件吗? @JohnJSmith 嗨,约翰,是的 :) 【参考方案1】:

正如 Google 应用内结算示例代码所说,您应该混淆这个公钥。

而不是仅仅将整个文字字符串存储在这里嵌入 程序,在运行时从片段构造密钥或 使用位操作(例如,与其他字符串异或)来隐藏 实际的钥匙。密钥本身不是秘密信息,但我们不 想让攻击者更容易用一个公钥替换公钥 他们自己的,然后从服务器伪造消息。

我使用非常简单的 Java 代码来生成 Java 类,它将把公钥还给我。基本思想是使用内部静态类使用递归来重新创建密钥。 仅供参考

对于我的利基市场来说,这是一种“足够好”的方法。有关混淆的更多信息,请参阅this stackexchange security question。

public static void main(String[] args) throws Exception 
    String className = genClassName();
    PrintWriter writer = new PrintWriter("C:\\" + className + ".java", "iso-8859-1");
    printClass(className, writer, "XXXXXX-YOUR-PUBLIC-KEY-GOES-HERE-XXXXXXX", true);
    writer.close();


private static String genClassName() 
    return "Class" + UUID.randomUUID().toString().replaceAll("-", "");


private static String printClass(String thisClass, PrintWriter writer, String key, boolean root) 
    int split = key.length() / 2;
    if (split < 10) 
        writer.println("public " + (root ? "" : "static") + " class " + thisClass + " ");
        writer.println("public static String get() ");
        writer.println("return \"" + key + "\";");
        writer.println("");
        writer.println("");
     else 
        String first = key.substring(0, split);
        String last = key.substring(split, key.length());
        writer.println("public " + (root ? "" : "static") + " class " + thisClass + " ");
        String class1 = printClass(genClassName(), writer, first, false);
        String class2 = printClass(genClassName(), writer, last, false);
        writer.println("public static String get() ");
        writer.println("return " + class1 + ".get() + " + class2 + ".get();");
        writer.println("");
        writer.println("");
    

    return thisClass;

【讨论】:

【参考方案2】:

如果您的应用程序中有一个服务器组件,那么您可以将大部分安全元素(包括您的公钥)移至您的服务器。在服务器上,您可以生成随机数并验证购买(我已将我的移动到 RESTFul WCF 服务)。如果您的服务器组件是基于 .NET 的,那么您可能必须从您的公钥生成一个模数和一个指数,以便您可以使用 RNGCryptoServiceProvider 类。有一个 Google I/O 视频,其中概述了 to In-App Billing 等。

【讨论】:

~"模数和指数是什么意思?它是否适用于应用内结算的 base64 编码密钥?【参考方案3】:

您需要程序源代码中的公钥,以便检查签名。是的,破解者会发现它,将其替换为假货,并为您的程序提供假货购买,这是非零且不可避免的风险。

您无法完全隐藏密钥以防窥探,但您可以混淆。您可以将 Base64 字符串分解为不同位置的多个字符串常量,并在使用前将它们连接起来。最好给块起一个不起眼的名字(不是MY_PUBLIC_KEY_PART_4)。您还可以对其应用额外的软加密层 - 例如 XOR a value。您可以添加完整性检查 - 确保密钥没有被欺骗(例如,将密钥的哈希存储在其他地方并检查)。但这仍然是通过默默无闻的安全性 - 一个足够坚定的黑客会通过。

还可以考虑内置代码混淆工具 ProGuard。

【讨论】:

您不需要隐藏或混淆公钥。 Google 将使用密钥(您(开发者)没有也不会出现在您的应用程序中)签署请求。公钥仅用于验证该签名。破解者无法使用公钥创建虚假购买。 破解者可以将应用内存中的公钥替换为自己的,并使用自己的私钥对购买进行签名。这就是 Google 建议您进行混淆处理的原因。 你说得对,我以为你在谈论钥匙本身。【参考方案4】:

您的 android 开发者控制台中的公钥(可在“编辑配置文件”下找到)已经经过 Base64 编码。只需将密钥的内容复制粘贴到源文件中即可。例如,如果你有这样的事情:

然后在你的Security.java:

String base64EncodedPublicKey = "MIIBIjANBgkqhkiG9w0BAQ......";

【讨论】:

base64EncodedPublicKey 应该是您的应用程序的公钥(您从 Google Play 开发者控制台获得)。这不是您的开发者公钥,而是 app-specific 公钥。不只是将整个文字字符串存储在这里嵌入程序中,而是在运行时从片段构造密钥或使用位操作(例如,与其他字符串进行异或)来隐藏实际密钥。密钥本身不是秘密信息,但我们不想让攻击者很容易用自己的公钥替换公钥,然后从服务器伪造消息。 过去我们有一个唯一的开发者密钥,用于许可和应用内。 您用于许可和应用内计费的 Base64 编码 RSA 公钥已移动,现在可以通过选择您的应用程序然后单击“服务和 API”选项卡在 Goggle Play 中找到。 Google 特别建议:~"不要只存储嵌入在程序中的整个文字字符串,而是在运行时从片段构造密钥或使用位操作"

以上是关于Android 计费 - 在 Security.java 文件中,base64EncodedPublicKey 应该是编码值吗?的主要内容,如果未能解决你的问题,请参考以下文章

Android 上是不是总是有一个计费服务实例?

Android:在应用计费增值税

android N 无法在应用计费 AIDL 中编译

如何在 Android 中测试 Google Play 计费订阅(非应用内购买)

Android 应用内计费如何以及在何处指定付费功能费用

我们如何在服务器端验证 Android 应用内计费收据?