42

我以前从未实现过 In App Purchase,所以我使用了 MKStoreKit 包装器并有一个有效的实现。MKStoreKit 将 UserDefaults .plist 中的所有收据保存为 BOOL,因此盗版者可以非常简单地以“破解”状态分发应用内购买。首次购买后,可以分发捆绑包并重新创建 .plist 以启用 IAP 解锁。

我想扩展 MKStoreKit 以在 iOS 钥匙串中创建 In App Purchase 验证数据。是否有任何缺点或可能的原因导致付费用户失败、不可靠或任何其他原因导致这样做总体上是个坏主意?我知道盗版是不可避免的,我绝对不想疏远付费用户,但我觉得 UserDefaults .plist 太容易绕过了。

在我的场景中,购买时会在钥匙串中放入一个简单的字符串。这样,如果二进制文件被分发,则尚未启用可解锁项。当然,有可能想出一个解决方法,但需要更多的努力并知道如何找到 TRUE/FALSE 标志并使其始终返回正确的值。通过混淆,我什至可以使追踪它变得更加困难。

感谢您的所有见解,我很欣赏避免强制性不可避免的盗版,处理它的答复的答案。我对这个解决方案的技术可行性更感兴趣。

4

1 回答 1

54

我们在我们的应用程序中做到了这一点,它运行良好。这是一个免费应用程序,您可以升级到完整版本,我们将升级指示器存储在钥匙串中。升级指示符是您选择的任意字符串,但出于钥匙串的目的,它被视为密码,即 kSecValueData 的值在钥匙串中被加密。这种方法的一个很好的好处是,如果用户删除应用程序然后重新安装它,一切都会像魔术一样重新启用,因为钥匙串项目与应用程序分开存储。并且在用户默认值中存储一些东西的额外工作是如此之少,我们认为这是值得的。

以下是创建安全项目的方法:

NSMutableDictionary* dict = [NSMutableDictionary dictionary];

[dict setObject: (id) kSecClassGenericPassword  forKey: (id) kSecClass];
[dict setObject: kYourUpgradeStateKey           forKey: (id) kSecAttrService];
[dict setObject: kYourUpgradeStateValue         forKey: (id) kSecValueData];

SecItemAdd ((CFDictionaryRef) dict, NULL);

以下是查找安全项目以检查其值的方法:

NSMutableDictionary* query = [NSMutableDictionary dictionary];

[query setObject: (id) kSecClassGenericPassword forKey: (id) kSecClass];
[query setObject: kYourUpgradeStateKey          forKey: (id) kSecAttrService];
[query setObject: (id) kCFBooleanTrue           forKey: (id) kSecReturnData];

NSData* upgradeItemData = nil;
SecItemCopyMatching ( (CFDictionaryRef) query, (CFTypeRef*) &upgradeItemData );
if ( !upgradeItemData )
{
    // Disable feature
}
else
{
    NSString* s = [[[NSString alloc] 
                        initWithData: upgradeItemData 
                            encoding: NSUTF8StringEncoding] autorelease];

    if ( [s isEqualToString: kYourUpgradeStateValue] )
    {
        // Enable feature
    }
}

如果 upgradeItemData 为 nil,则该键不存在,因此您可以假设升级不存在,或者,我们所做的,是放入一个表示未升级的值。

更新

添加了 kSecReturnData(感谢@Luis 指出)

GitHub 上的代码(ARC 变体)

于 2011-02-18T21:55:48.577 回答