61

来自 Android In App Billing 版本 3 (TrivialDrive) 示例应用程序附带 sdk

MainActivity.java

/* base64EncodedPublicKey should be YOUR APPLICATION'S PUBLIC KEY
 * (that you got from the Google Play developer console). This is not your
 * developer public key, it's the *app-specific* public key.
 *
 * Instead of just storing the entire literal string here embedded in the
 * program,  construct the key at runtime from pieces or
 * use bit manipulation (for example, XOR with some other string) to hide
 * the actual key.  The key itself is not secret information, but we don't
 * want to make it easy for an attacker to replace the public key with one
 * of their own and then fake messages from the server.
 */
String base64EncodedPublicKey = "CONSTRUCT_YOUR_KEY_AND_PLACE_IT_HERE";

好吧,我不确定我是否了解此安全措施。我知道如何从 Google Play Developer Console 获取应用程序公钥(已经是 base 64 编码)。

我不明白的是这部分

 /* Instead of just storing the entire literal string here embedded in the
 * program,  construct the key at runtime from pieces or
 * use bit manipulation (for example, XOR with some other string) to hide
 * the actual key
 */

据我所知,这个公钥是一个常量字符串,是谷歌在应用程序上传过程中给出的。

我们如何使用任何位操作过程以编程方式创建相同的密钥?以前有人做过吗?有没有关于如何做到这一点的示例代码?

4

7 回答 7

43

像这样的东西:

String Base64EncodedPublicKey key = "Ak3jfkd" + GetMiddleBit() + "D349824";

或者

String Base64EncodedPublicKey key = 
         DecrementEachletter("Bl4kgle") + GetMiddleBit() + ReverseString("D349824");

或任何不将密钥放在 base64 纯文本中的单个字符串中。可能也不将密钥存储在 base64 中的东西也是一个好主意,因为原始的 base64 文本片段很容易发现。

这不是保护密钥的特别好的方法。但它可以防止一些微不足道的攻击,即有人只是在您的 APK 中搜索文字字符串,寻找看起来像 base64 编码的公钥的东西。至少你让 #$ # $ers 工作了一点。

如果邪恶的人识别了您的公钥,他们可能会做坏事。显然,谷歌似乎是这么认为的。我可以猜到这一步的作用,但我不确定我是否真的想在一个开放的论坛上推测这一点,并给任何人任何想法。你想这样做。

基本情节摘要将是您使某人更难以编写以编程方式对应用程序进行 LVL 处理的应用程序。

假设任何这样做的人都以破解 20 或 30,000 个 Android 应用程序并重新发布它们为生。很有可能,我想他们不会再花十分钟的时间将您的应用程序添加到已被程序破坏的 20,000 个 Android 应用程序的列表中,如果他们实际上需要做一些手动工作的话。除非您有顶级应用程序。然后战斗可能是无休止的,而且可能最终是徒劳的。

将密钥分成连续的块(如另一个答案中所提议的)可能还不够好。因为密钥将在 APK 的字符串常量表中以连续的字符串结尾。用程序很容易找到它。

于 2013-01-16T07:56:49.340 回答
13

另一种方法是对键进行一些基本的转换。

// Replace this with your encoded key.
String base64EncodedPublicKey = "";

// Get byte sequence to play with.
byte[] bytes = base64EncodedPublicKey.getBytes();

// Swap upper and lower case letters.
for (int i = 0; i < bytes.length; i++) {
    if(bytes[i] >= 'A' && bytes[i] <= 'Z')
        bytes[i] = (byte)( 'a' + (bytes[i] - 'A'));
    else if(bytes[i] >= 'a' && bytes[i] <= 'z')
        bytes[i] = (byte)( 'A' + (bytes[i] - 'a'));
}

// Assign back to string.
base64EncodedPublicKey = new String( bytes );

所以想法是将您的原始密钥放入 asbase64EncodedPublicKey并运行上面的代码,它将交换大小写字母并将结果放回base64EncodedPublicKey. base64EncodedPublicKey然后,您可以从调试器复制结果并将其作为原始值粘贴到代码中。此时您的密钥将被转换(大写和小写切换),并且在运行时它会将其修复回正确的大小写,并继续工作。

以上显然是一个相当基本的转码,但您可以更有创意,颠倒AZ的顺序,交换奇偶数,将元音交换为偶数。这里的问题是,如果我将代码放在上面的代码片段中进行了一堆更有趣的转码,然后每个人都将其复制并粘贴到他们的项目中,破解者将很容易能够自己查看和使用转码(通过查看这个帖子)!所以你只需要自己想出一些变换。

我特意在两个方向上进行了上述工作(因此,如果您运行两次,您将获得原始值),因为它可以轻松地在原始密钥上运行算法。我认为它有点整洁,看起来真正的钥匙以纯文本的形式放在那里,一个随便的破解者可能会尝试切换它,然后当它不起作用时会感到困惑。

于 2015-02-20T10:41:00.343 回答
5

你可以像这样把它分成几块

String piece1 = "SDFGJKGB4UIH234WE/FRT23RSDF/3DFUISDFVWE";
String piece2 = "SDFGJKGB4UIHUISDFVWE";
String piece3 = "BDYASGBDNAWGRET24IYE23das4saGBENWKD";
String piece4 = "432423SDF23R/+SDDS";

mHelper = new IabHelper(this, piece1 + piece2 + piece3 + piece4);

任何类型的操作都可以。

您无法完美地向攻击者隐藏公钥,您只需要操纵字符串来迷惑攻击者一点点

您可以添加一些字符串并在需要时将其删除或将其拆分为块。

于 2013-01-16T07:38:40.613 回答
2

我所做的是将密钥转换为 char 数组,将其分成两部分,然后在需要时重建它,如下所示:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_shop);

    char[] base64KeyByteArray = ArrayUtils.addAll(getPublicKeyChunk1(), getPublicKeyChunk2());

    Log.d(TAG, String.valueOf(base64KeyByteArray));
}

private char[] getPublicKeyChunk1() {
    return new char[]{82, 73, 67, 66, 73, 106, 65, 78, 66, 103, 107, 113, 104, 107,
            105, 71, 57, 119, 79, 66, 65, 81, 69, 70, 65, 65, 79, 67, 65, 81, 56, 65, 77, 73,
            73, 66, 67, 103, 75, 67, 65, 81, 69, 65, 121, 55, 81, 76, 122, 67, 105, 80, 65,
            110, 105, 101, 72, 66, 53, 57};
}

private char[] getPublicKeyChunk2() {
    return new char[]{82, 43, 68, 47, 79, 121, 122, 110, 85, 67, 118, 89, 108, 120, 43, 49,
            80, 100, 67, 108, 55, 90, 57, 103, 119, 57, 87, 78, 79, 111, 53, 101, 80, 71,
            117, 74, 104, 82, 87, 97, 100};
}
于 2016-12-20T09:12:36.927 回答
1

在我从gidds获得的帮助下,以Steven Craft 的回答为基础

这是一个更干净的代码。此外,它将数字 (0-9) 换成 ascii 字符 (37-46),反之亦然。用科特林写的。

 val string = "Hello World 012345679 %&()"
 fun String.swapCase() = map {
        when {
            it.isUpperCase() -> it.toLowerCase()
            it.isLowerCase() -> it.toUpperCase()
            it.isDigit() -> (37 + (it.toInt() - 48)).toChar()
            it.isDefined() -> if (it.toInt() >= 37 && it.toInt() <= 46) (48 + (it.toInt() - 37)).toChar() else it
            else -> it
        }
    }.joinToString("")
    println(string.swapCase()) // hELLO wORLD %&'()*+,. 0134

使用这个-> https://edge-developer.github.io/BillingGenerator/快速生成所有这些

于 2019-04-07T11:43:20.103 回答
0

遵循 3 个简单的步骤来保护 API/密钥

我们可以使用 Gradle 来保护 API 密钥或 Secret 密钥。检查我的答案

于 2017-11-29T12:57:39.727 回答
-3

有人真的需要你的私钥吗?我认为整个想法是替换它。恕我直言,任何操作都是无用的。邪恶的人唯一要做的就是用正确的(他自己的密钥)值初始化变量一行begore google API调用。

于 2016-04-16T22:17:46.957 回答