在应用程序钥匙串中存储应用内购买收据
Posted
技术标签:
【中文标题】在应用程序钥匙串中存储应用内购买收据【英文标题】:Storing In App Purchase receipts in the application Keychain 【发布时间】:2011-06-26 02:49:13 【问题描述】:我以前从未实现过 In App Purchase,所以我使用了 MKStoreKit 包装器并有一个有效的实现。 MKStoreKit 将 UserDefaults .plist 中的所有收据保存为 BOOL,因此盗版者可以非常简单地以“破解”状态分发应用内购买。首次购买后,即可分发捆绑包并重新创建 .plist 以启用 IAP 解锁。
我想扩展 MKStoreKit 以在 ios 钥匙串中创建 In App Purchase 验证数据。是否有任何缺点或可能的原因导致付费用户失败、不可靠或任何其他原因导致这样做总体上是个坏主意?我知道盗版是不可避免的,我绝对不想疏远付费用户,但我觉得 UserDefaults .plist 太容易绕过了。
在我的场景中,购买时会在钥匙串中放入一个简单的字符串。这样,如果二进制文件被分发,则尚未启用可解锁项。当然,有可能想出一个解决方法,但需要更多的努力并知道如何找到 TRUE/FALSE 标志并使其始终返回正确的值。通过混淆,我什至可以稍微提高追踪它的难度。
感谢您的所有见解,我感谢您避免强制性不可避免的盗版,处理它的答复。我对这个解决方案的技术可行性更感兴趣。
【问题讨论】:
+1 这与我的兴趣相关。目前,我将一些字符串(作为盐)添加到设备标识符和 md5 中,并将其存储在 userdefaults 中。 非常酷。这样它就不会在没有 iTunes 凭据的情况下在另一台设备上进行身份验证。 作为记录,我不确定您是否参与其中,但 MKStoreKit 现在会在 iOS 钥匙串中创建验证数据。 是的,这篇文章是在那之前发布的 为了记录,@MatthiasBauch 的方法可能是个坏主意——如果用户升级到新设备并从备份中恢复,则设备 ID(或任何 UUID 替换做同样的事情是称为)将不匹配!它看起来像盗版,因此用户至少必须恢复购买 - 或者,如果您在认为它是盗版时做的更公开,那么在这种情况下可能会适得其反。跨度> 【参考方案1】:我们在我们的应用程序中就是这样做的,而且效果很好。这是一个免费应用程序,您可以升级到完整版本,我们将升级指示器存储在钥匙串中。升级指示符是您选择的任意字符串,但出于钥匙串的目的,它被视为密码,即 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 指出)
Code on GitHub (ARC variant)
【讨论】:
很好的答案,非常感谢 好东西。更多的人需要使用他们的应用程序来做到这一点。如此多的应用程序只是将内容存储在 plist 中。它可以使用 iExplorer 等工具轻松编辑,并且需要越狱设备。 您可能需要在调用 SecItemCopyMatching 之前添加[query setObject:(id) kCFBooleanTrue forKey: (id) kSecReturnData];
如果你的应用在后台运行(即导航应用),你应该添加:[dict setObject: (id) kSecAttrAccessibleAfterFirstUnlock forKey: (id) kSecAttrAccessible];
否则,keychain 项会在设备一进入状态时突然变得不可访问,其中您必须输入您的密码才能解锁。
你们建议如何设置 kYourUpgradeStateKey 和 kYourUpgradeStateValue 例如购买专业版。只是作为一个例子,让我了解可能性。以上是关于在应用程序钥匙串中存储应用内购买收据的主要内容,如果未能解决你的问题,请参考以下文章