Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Custom SSKeychainQuery subclass that supports keychain sharing on OS X
// KeychainQuery is OS X only.
#if os(OSX)
/// Custom SSKeychainQuery subclass that supports keychain sharing on OS X.
/// based on (Listing 3-2)
private class KeychainQuery: SSKeychainQuery {
/// Specify app paths to share keychain with.
/// No need to pass current app's path (which calls the code).
var sharedAppPaths: [String]?
override func save(errorPtr: NSErrorPointer) -> Bool {
if let sharedAppPaths = sharedAppPaths where sharedAppPaths.count > 0 {
if service == nil || account == nil || password == nil {
addError(KeychainQuery.errorWithCode(SSKeychainErrorCode.BadArguments.rawValue), toPointer: errorPtr)
return false
let searchQuery = query()
var status = SecItemCopyMatching(searchQuery, nil)
switch status {
case errSecSuccess:
consoleLog("|Keychain| Item already present, just updating the value.")
status = SecItemUpdate(searchQuery, [(kSecValueData as! String): passwordData])
case errSecItemNotFound: // create new then
let (access, accStatus) = createAccess()
status = (access >>> addItemWithAccess) ?? accStatus
default: break
let error = KeychainQuery.errorWithCode(status)
addError(error, toPointer: errorPtr)
return error == nil
} else {
// fallback to original implementation if no paths specified
private func createAccess() -> (SecAccess?, OSStatus) {
consoleLog("|Keychain| Creating custom access list")
var status = noErr
var apps = [Unmanaged<SecTrustedApplication>?](count: (sharedAppPaths?.count ?? 0) + 1, repeatedValue: nil)
status = SecTrustedApplicationCreateFromPath(nil, &apps[0]) // add this app
if let sharedAppPaths = sharedAppPaths {
for (index, path) in enumerate(sharedAppPaths) {
if status != noErr { break }
status = SecTrustedApplicationCreateFromPath(path, &apps[index + 1])
var access: Unmanaged<SecAccess>?
if status == noErr {
var appsPointer = UnsafeMutablePointer<UnsafePointer<Void>>( { $0!.takeUnretainedValue() })
let cfArr = CFArrayCreate(nil, appsPointer, apps.count, nil)
status = SecAccessCreate(service, cfArr, &access)
return (access?.takeUnretainedValue(), status)
private func addItemWithAccess(access: SecAccess) -> OSStatus {
consoleLog("|Keychain| Creating item with custom ACL.")
var attrs: [SecKeychainAttribute] = []
createAttribute(kSecLabelItemAttr, label) >>> attrs.append
createAttribute(kSecAccountItemAttr, account) >>> attrs.append
createAttribute(kSecServiceItemAttr, service) >>> attrs.append
var attrList = SecKeychainAttributeList(count: UInt32(attrs.count), attr: &attrs)
let pass = (password as NSString).UTF8String
let len = UInt32(truncatingBitPattern: strlen(pass))
return SecKeychainItemCreateFromContent(kSecGenericPasswordItemClass, &attrList, len, pass, nil, access, nil)
private func createAttribute(tag: Int, _ data: String?) -> SecKeychainAttribute? {
if var data = data {
let utfString = UnsafeMutablePointer<Int8>((data as NSString).UTF8String)
let len = UInt32(truncatingBitPattern: strlen(utfString))
return SecKeychainAttribute(tag: SecKeychainAttrType(tag), length: len, data: utfString)
} else {
return nil
private func addError(error: NSError?, toPointer pointer: NSErrorPointer) {
if let error = error where pointer != nil {
pointer.memory = error

This comment has been minimized.

Copy link
Owner Author

rinatkhanov commented Jun 30, 2015

  • You need to expose some of the SSKeychainQuery's private methods to public header to make this subclass work.
  • This code allows creating new items with custom Access Lists in the keychain, but not updating existing items (though there's a way).
  • >>> is a custom flatMap operator (well, sort of). It simply unwraps the optional value on the left and passes it to the function on the right if there's a value, and returns nil otherwise.
  • The code works, but it may be error-prone, so take it with a grain of salt.

This comment has been minimized.

Copy link

dvng88 commented Aug 24, 2017

Hello, I am getting too many errors. 'SSKeychainQuery' is it Objective-C file, I am trying with that and no luck. Any suggestion? Also I am trying with Xcode 8.3 and Swift3, is it correct?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.