【问题标题】:Access App Identifier Prefix programmatically以编程方式访问应用标识符前缀
【发布时间】:2012-07-28 10:13:55
【问题描述】:

如何以编程方式访问 Bundle Seed ID/Team ID/App Identifier Prefix 字符串? (据我所知,这些都是一样的)。

我正在使用 UICKeychainStore 钥匙串包装器在多个应用程序中保存数据。这些应用程序中的每一个在其权利 plist 中都有一个共享的钥匙串访问组,并共享相同的配置文件。默认情况下,钥匙串服务使用 plist 中的第一个访问组作为保存数据的访问组。当我调试 UICKeychainStore 时,这看起来像“AS234SDG.com.myCompany.SpecificApp”。我想将访问组设置为“AS234SDG.com.myCompany.SharedStuff”,但我似乎无法找到如何以编程方式获取访问组的“AS234SDG”字符串,并且希望避免对其进行硬编码如果可能的话。

【问题讨论】:

  • 我不知道我是否理解你,但它是不是这个 NSString *bundleIDStr = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleIdentifier"];你在找吗?
  • 这将返回“com.myCompany.SpecificApp” - 我正在寻找“AS234SDG”前缀。
  • 哦,我明白你现在要问的了...
  • 这是个好问题。我也找不到这个。

标签: iphone objective-c ios cocoa-touch ipad


【解决方案1】:

Info.plist 可以有你自己的信息,如果你写一个带有$(AppIdentifierPrefix) 的值,它会在构建阶段替换为真正的应用标识符前缀。

那么,试试这个:

在您的 Info.plist 中,添加有关应用标识符前缀的信息。

<key>AppIdentifierPrefix</key>
<string>$(AppIdentifierPrefix)</string>

然后您可以使用 Objective-C 以编程方式检索它:

NSString *appIdentifierPrefix =
    [[NSBundle mainBundle] objectForInfoDictionaryKey:@"AppIdentifierPrefix"];

使用 Swift:

let appIdentifierPrefix =
    Bundle.main.infoDictionary!["AppIdentifierPrefix"] as! String

注意appIdentifierPrefix 以句点结尾;例如AS234SDG.

【讨论】:

  • 这似乎是最好的方法。您甚至可以存储权利中显示的完整值,例如$(AppIdentifierPrefix)com.MyCompany.MyApp,然后直接使用它而无需任何进一步修改。
  • 同意这是首选。标记为答案的编程方法在 2012 年可能很好,但我看到代码在 2015 年在 iOS 8 上运行不一致。
  • 这应该是首选的方式。如果启用数据保护并粉碎钥匙串访问组,则使用安全框架检索它时会出现一些问题。
  • 这对我在 XCode 5.1.1 上不起作用。 $(AppIdentifierPrefix) 只是返回空白。在打包构建选项中打开预处理 info.plist 设置没有任何区别。
  • XCode 8 beta 已经打破了这个构建时间替换,至少在 beta 2 中是这样的
【解决方案2】:

您可以通过查看现有 KeyChain 项的访问组属性(即kSecAttrAccessGroup)以编程方式检索捆绑种子 ID。在下面的代码中,我查找现有的 KeyChain 条目,如果不存在则创建一个。一旦我有一个 KeyChain 条目,我就会从中提取访问组信息,并返回访问组的第一个由“。”分隔的组件。 (句点)作为捆绑种子 ID。

+ (NSString *)bundleSeedID {
    NSString *tempAccountName = @"bundleSeedID";
    NSDictionary *query = @{
        (__bridge NSString *)kSecClass : (__bridge NSString *)kSecClassGenericPassword,
        (__bridge NSString *)kSecAttrAccount : tempAccountName,
        (__bridge NSString *)kSecAttrService : @"",
        (__bridge NSString *)kSecReturnAttributes: (__bridge NSNumber *)kCFBooleanTrue,
    };
    CFDictionaryRef result = nil;
    OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&result);
    if (status == errSecItemNotFound)
        status = SecItemAdd((__bridge CFDictionaryRef)query, (CFTypeRef *)&result);
    if (status != errSecSuccess) {
        return nil;
    }
    status = SecItemDelete((__bridge CFDictionaryRef)query); // remove temp item
    NSDictionary *dict = (__bridge_transfer NSDictionary *)result;
    NSString *accessGroup = dict[(__bridge NSString *)kSecAttrAccessGroup];
    NSArray *components = [accessGroup componentsSeparatedByString:@"."];
    NSString *bundleSeedID = [[components objectEnumerator] nextObject];
    return bundleSeedID;
}

【讨论】:

  • 只是好奇,您是如何找到使用“bundleSeedID”作为 kSecAttrAccount 的?
  • @RajPara:这只是我选择的一个随机值。如果需要,您可以将其更改为“com.acme.bundleSeedID”。重点是在 KeyChain 中创建一个条目并读取它并从访问组信息中提取捆绑种子 ID。
  • 这是唯一的解决方案吗?它有效,但只是好奇是否没有其他方法。
  • @orange80:可以打开“embedded.mobileprovision”文件提取App ID。但是,由于它封装在 CMS 结构中并且 CMS api 在 iOS 上不公开,因此您可以使用以下选项提取 plist 有效负载:1.) 进行一些搜索和替换预处理以去除 CMS 标头/签名, 2.) 嵌入 OpenSSL 并使用其 CMS api 来提取有效负载,3.) 阅读 CMS 规范并自己构建自己的有效负载提取器。另外,这是另一种可能的解决方案(我不知道它是否有效):stackoverflow.com/a/5240079/81046
  • 这个解决方案似乎不再适用 - 至少在模拟器中的 iOS 7 上不起作用。
【解决方案3】:

这是@David H 答案的 Swift 版本:

static func bundleSeedID() -> String? {
        let queryLoad: [String: AnyObject] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrAccount as String: "bundleSeedID" as AnyObject,
            kSecAttrService as String: "" as AnyObject,
            kSecReturnAttributes as String: kCFBooleanTrue
        ]

        var result : AnyObject?
        var status = withUnsafeMutablePointer(to: &result) {
            SecItemCopyMatching(queryLoad as CFDictionary, UnsafeMutablePointer($0))
        }

        if status == errSecItemNotFound {
            status = withUnsafeMutablePointer(to: &result) {
                SecItemAdd(queryLoad as CFDictionary, UnsafeMutablePointer($0))
            }
        }

        if status == noErr {
            if let resultDict = result as? [String: Any], let accessGroup = resultDict[kSecAttrAccessGroup as String] as? String {
                let components = accessGroup.components(separatedBy: ".")
                return components.first
            }else {
                return nil
            }
        } else {
            print("Error getting bundleSeedID to Keychain")
            return nil
        }
    }

【讨论】:

  • 适用于 Xcode 10 beta2,而来自Info.plistAppIdentifierPrefix 给了我nil
【解决方案4】:

这是一个很好的问题,但要实现您的目标,本可以有一个解决方案 不需要检索捆绑种子 ID。

来自article,与您使用的钥匙串包装器大致相同:

默认情况下,它将选择您指定的第一个访问组 Entitlements.plist 写入时将搜索所有 未指定时访问组。

然后将在授予访问权限的所有组中搜索密钥。 因此,为了解决您的问题,您可以将所有捆绑应用程序的访问组添加到您的 entitlements.plist 中,而不是使用“共享的东西”组,将 $(CFBundleIdentifier) 作为您的第一个钥匙串组(然后您的钥匙串包装器将在此写入组),一切就绪

【讨论】:

    猜你喜欢
    • 2011-01-22
    • 2013-12-12
    • 1970-01-01
    • 2016-12-11
    • 2013-12-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-09
    相关资源
    最近更新 更多