在应用程序钥匙串中存储应用内购买收据

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 例如购买专业版。只是作为一个例子,让我了解可能性。

以上是关于在应用程序钥匙串中存储应用内购买收据的主要内容,如果未能解决你的问题,请参考以下文章

Firebase Firestore +云功能服务器端验证应用内购买收据(Swift + NodeJS)

iOS 7:应用内购买收据验证和验证

应用内购买验证收据错误

iOS 应用内购买收据与不同的 iTunes 用户

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

在钥匙串中存储凭据不仅加密密码