Keychain Access
中有一个选项允许所有应用程序无限制地访问钥匙串项。
我不知道如何以编程方式设置它。我尝试使用空ACL创建和设置新的SecAccessRef,并没有真正改变任何内容(使用SecItemUpdate
更新kSecAttrAccess
)。我还尝试获取项目的所有授权标记的所有ACL列表,并将ACL内容设置为该ACL /标记组合的空数组。我能够清除允许的应用列表,但这并没有允许所有应用程序无限制地访问项目。我没有看到使用keychain api设置此方法的方法。
所以我的问题是如何操纵Access对象或其ACL以允许不受限制地访问钥匙串项或至少不受限制的读取?
答案 0 :(得分:1)
根据我的经验,大多数最近的"便利性" Security API中将项目存入钥匙串的方法:
SecKeychainAddGenericPassword()
强> SecKeychainAddInternetPassword()
强> SecKeyGeneratePair()
强> 在密码上添加change_acl
授权ACL条目,使用空数组的可信应用程序添加私钥 - 这意味着没有应用程序可以在没有用户提示的情况下随后修改ACL。
因此,通常情况下,您似乎无法在没有用户提示的情况下修改大多数现有钥匙串项目的ACL。
但,如果您使用较旧的安全API方法将项目添加到钥匙串(您可以向其提供自己创建的SecAccessRef
):
SecKeychainItemCreateFromContent()
SecKeyCreatePair()
(已弃用
OS X 10.7但仍然有效)然后您可以有效地将这些项目设置为"允许所有应用程序访问此项目"根据您的应用,这可能很有用。
也就是说,对于这些旧功能,您可以:
SecAccessRef
或 SecAccessCreate()
SecAccessCreateWithOwnerAndACL()
SecAccessRef
授权的Any
添加单个ACL,不使用提示行为(SecKeychainPromptSelector = 0
)和NULL
受信任的应用列表} 强> 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的更多信息,请参阅文档old或new (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。