如何允许所有应用程序在没有提示的情况下访问keychain项目

时间:2017-01-24 16:08:46

标签: macos keychain

Keychain Access中有一个选项允许所有应用程序无限制地访问钥匙串项。 Keychain Access - Access Control tab

我不知道如何以编程方式设置它。我尝试使用空ACL创建和设置新的SecAccessRef,并没有真正改变任何内容(使用SecItemUpdate更新kSecAttrAccess)。我还尝试获取项目的所有授权标记的所有ACL列表,并将ACL内容设置为该ACL /标记组合的空数组。我能够清除允许的应用列表,但这并没有允许所有应用程序无限制地访问项目。我没有看到使用keychain api设置此方法的方法。

所以我的问题是如何操纵Access对象或其ACL以允许不受限制地访问钥匙串项或至少不受限制的读取?

3 个答案:

答案 0 :(得分:1)

根据我的经验,大多数最近的"便利性" Security API中将项目存入钥匙串的方法:

  • SecKeychainAddGenericPassword()
  • SecKeychainAddInternetPassword()
  • SecKeyGeneratePair()

在密码上添加change_acl授权ACL条目,使用空数组的可信应用程序添加私钥 - 这意味着没有应用程序可以在没有用户提示的情况下随后修改ACL。

因此,通常情况下,您似乎无法在没有用户提示的情况下修改大多数现有钥匙串项目的ACL。

,如果您使用较旧的安全API方法将项目添加到钥匙串(您可以向其提供自己创建的SecAccessRef):

  • SecKeychainItemCreateFromContent()
  • SecKeyCreatePair() (已弃用 OS X 10.7但仍然有效)

然后您可以有效地将这些项目设置为"允许所有应用程序访问此项目"根据您的应用,这可能很有用。

也就是说,对于这些旧功能,您可以:

  1. 使用 SecAccessRef SecAccessCreate()
  2. 创建SecAccessCreateWithOwnerAndACL()
  3. 使用 {{1}向使用SecAccessRef授权的Any添加单个ACL,不使用提示行为(SecKeychainPromptSelector = 0)和NULL受信任的应用列表}
  4. 使用上述API创建钥匙串项目时传递SecACLCreateWithSimpleContents()以实现"允许所有应用程序访问此项目"

答案 1 :(得分:1)

正如@ mike-c在他的回答中所述,似乎该解决方案无法使用为支持iOS和iCloud而首先引入的较新的Keychain API来实现。特别是访问控制API不再与macOS对待它的原始方式兼容。除此之外,还引入了许多不同的API,如session 711 from WWDC 2014: Keychain and Authentication with Touch ID(从09:40开始)所示,这使得理解更加困难。

@ mike-c的答案包含许多提示,但未显示完整图片。与Apple开发者论坛上的this question类似,它告诉您,必须在创建时为钥匙串项设置适当的访问控制。不幸的是,新的文档中不再提及旧的Keychain API,因此我不得不深入研究Keychain Services Programming Guide的存档版本。

创建访问对象

要描述访问配置,我们必须使用具有关联访问控制列表(SecAccess)的所谓访问对象(SecACL)。要了解有关ACL的更多信息,请参阅文档oldnew (and less detailed)版本中的说明。出于此答案的目的,ACL定义了哪个应用程序可以访问给定操作的钥匙串项。

使用SecAccessCreate(…)可以基于提供的参数创建具有预定义系统默认配置的新访问对象。默认情况下,它包含三个ACL。我试图按照@ mike-c的答案中的建议添加新的ACL,但遇到一种奇怪的行为,有时授予该项目访问权限,有时不授予该访问权限。

this documentation page的“高级主题”部分中介绍了实现此目的的方法。基本上,我们不添加新的ACL,而是修改现有的ACL。

此功能创建一个访问对象,该对象配置为所有应用程序都具有不受限制的访问权限。它是使用Swift编写的,保留了Keychain C API的样式。这很尴尬,但是这次赢得了一致。

/// Creates an access object with the system default configuration which has an altered ACL
/// to allow access for all applications.
///
/// - Parameter descriptor: The name of the item as it should appear in security dialogs.
/// - Parameter accessRef: The pointer to the new access object.
/// - Returns: A result code.
func SecAccessCreateForAllApplications(
    descriptor: CFString,
    accessRef outerAccessRef: UnsafeMutablePointer<SecAccess?>
) -> OSStatus {
    var accessRef: SecAccess?

    // Create an access object with access granted to no application (2nd parameter).
    // It comes configured with 3 default ACLs.
    let accessCreateStatus = SecAccessCreate(
        descriptor,
        [] as CFArray, // No application has access
        &accessRef
    )

    guard accessCreateStatus == errSecSuccess else { return accessCreateStatus }
    guard let access = accessRef else { return accessCreateStatus }

    // Extract the default ACLs from the created access object for the *decrypt* authorization tag.
    guard let aclList = SecAccessCopyMatchingACLList(
        access,
        kSecACLAuthorizationDecrypt
    ) as? [SecACL] else { return errSecInvalidACL }

    // There should be exactly one ACL for the *decrypt* authorization tag.
    guard aclList.count == 1 else { return errSecInvalidACL }
    guard let decryptACL = aclList.first else { return errSecInvalidACL }

    // Extract all authorizations from the default ACL for the *decrypt* authorization tag.
    let allAuthorizations = SecACLCopyAuthorizations(decryptACL)

    // Remove the default ACL for the *decrypt* authorization tag from the access object.
    let aclRemoveStatus = SecACLRemove(decryptACL)

    guard aclRemoveStatus == errSecSuccess else { return aclRemoveStatus }

    // Create a new ACL with access for all applications and add it to the access object.
    var newDecryptACLRef: SecACL?
    let aclCreateStatus = SecACLCreateWithSimpleContents(
        access,
        nil, // All applications have access
        descriptor,
        [], // Empty prompt selector
        &newDecryptACLRef
    )

    guard aclCreateStatus == errSecSuccess else { return aclCreateStatus }
    guard let newDecryptACL = newDecryptACLRef else { return aclCreateStatus }

    // Set the authorizations extracted from the default ACL to the newly created ACL.
    let aclUpdateAuthorizationStatus = SecACLUpdateAuthorizations(newDecryptACL, allAuthorizations)

    guard aclUpdateAuthorizationStatus == errSecSuccess else { return aclUpdateAuthorizationStatus }

    // Finally, write the access to the outer pointer.
    outerAccessRef.initialize(to: access)

    return errSecSuccess
}

写作钥匙扣物品

第二部分相当简单。事实证明,使用SecItemAdd(…)创建钥匙串项时可以指定访问对象。将自定义访问对象作为键kSecAttrAccess的值放入属性字典,它将被应用。

此代码将在配置了自定义访问对象的情况下写入钥匙串项:

var accessRef: SecAccess?

let accessCreateStatus = SecAccessCreateForAllApplications(
    descriptor: "" as CFString, // Actually not necessary
    accessRef: &accessRef
)

guard accessCreateStatus == errSecSuccess else { exit(EXIT_FAILURE) }
guard let access = accessRef else { exit(EXIT_FAILURE) }

let attributes: [CFString: Any] = [
    kSecClass: kSecClassGenericPassword,
    kSecAttrAccount: "Foo",
    kSecValueData: "bar".data(using: .utf8)!,
    kSecAttrAccess: access
]

let itemAddStatus = SecItemAdd(attributes as CFDictionary, nil)

guard itemAddStatus == errSecSuccess else { exit(EXIT_FAILURE) }

读取钥匙串项

在“钥匙串访问”应用程序中显示“钥匙串”项目时,该项目已正确标记为可供所有应用程序访问。

您可以在不显示任何对话框的情况下从另一个应用程序读取项目。这是读取和解密物品有效载荷的代码:

let attributes: [CFString: Any] = [
    kSecClass: kSecClassGenericPassword,
    kSecAttrAccount: "Foo",
    kSecReturnData: kCFBooleanTrue as Any,
    kSecMatchLimit: kSecMatchLimitOne
]

var dataRef: AnyObject?
let status = SecItemCopyMatching(attributes as CFDictionary, &dataRef)

guard status == errSecSuccess else { exit(EXIT_FAILURE) }
guard let data = dataRef as? Data else { exit(EXIT_FAILURE) }
guard let string = String(data: data, encoding: .utf8) else { exit(EXIT_FAILURE) }

print(string) // => bar

我仍然没有掌握传统的Keychain API,因此欢迎您对所提供的方法提供任何反馈。

答案 2 :(得分:0)

虽然Apple文档中似乎缺少这一点,但提供给SecKeyGeneratePair()的参数字典可以具有kSecAttrAccess的关键字以及提供所需的SecAccessRef的关联值访问控制列表。在初始生成时提供此选项将允许在没有用户提示的情况下设置ACL。