Objective-C utility class for storing data securely in the key chain.

Related tags

Keychain Lockbox
Overview

Lockbox

Build Status CocoaPods CocoaPods CocoaPods

Lockbox is an Objective-C utility class for storing data securely in the keychain. Use it to store small, sensitive bits of data securely.

Looking for a Swift version? Check out Strongbox.

Overview

There are some bits of data that an app sometimes needs to store that are sensitive:

  • Usernames
  • Passwords
  • In-App Purchase unlocked feature bits
  • and anything else that, if in the wrong hands, would be B-A-D.

The thing to realize is that data stored in NSUserDefaults is stored in the clear! For that matter, most everything stored in your app's sandbox is also there in the clear.

Surprisingly, new and experienced app developers alike often do not realize this, until it's too late.

The Lockbox class methods make it easy to store and retrieve NSStrings, NSArrays, NSSets, NSDictionarys, and NSDates any Foundation-based object that conforms to NSSecureCoding into and from the key chain. You are spared having to deal with the keychain APIs directly!

For greater security, and to avoid possible collisions between data stored by your app with data stored by other apps (yours or other developers), the keys you provide in the class methods for storing and retrieving data are prefixed with your app's bundle id. The class methods provide some convenience by simplifying the use of Lockbox. But if you need to be able to access a common set of keys between your app, and say, an iOS8 extension, you may need to override the key prefix. For that, you can instantiate your own instance of Lockbox, providing your custom key prefix, and call the same methods (as instance methods) as you would call on the class. (The signatures are the same between class and instance methods. In fact, the class methods operate on a class-static Lockbox instance.)

The one caveat to keep in mind is that the keychain is really not meant to store large chunks of data, so don't try and store a huge array of data with these APIs simply because you want it secure. In this case, consider alternative encryption techniques.

Methods

Lockbox 3 includes the following methods, shown here as class methods. The same methods (as instance methods) may be called on your own Lockbox instances.

General object storage and retrieval

  • +archiveObject:forKey:
  • +archiveObject:forKey:accessibility:
  • +unarchiveObject:forKey:

These methods use an NSKeyedArchiver and NSKeyedUnarchiver, respectively, to encode and decode your objects. Your objects must conform toe NSSecureCoding.

The +archiveObject:forKey:... methods return BOOL indicating if the keychain operation succeeded or failed. The method +unarchiveObjectForKey: method returns a non-nil value on success or nil on failure. The returned value is of type id, but you assign this to whatever you know the data type should be. For example:

NSDate *today = [NSDate date];
[Lockbox archiveObject:today forKey:@"theDate"];
...

NSDate *theDate = [Lockbox unarchiveObjectForKey:@"theDate"];

See below for notes on the accessibility argument.

See below for information about migrating from Lockbox v2.x APIs to Lockbox v3 APIs.

Lockbox 2.x and older include the following deprecated methods, shown here as class methods. The same methods (as instance methods) may be called on your own Lockbox instances.

NSString (deprecated)

  • +setString:forKey:
  • +setString:forKey:accessibility:
  • +stringForKey:

NSArray (deprecated)

  • +setArray:forKey:
  • +setArray:forKey:accessibility:
  • +arrayForKey:

NSSet (deprecated)

  • +setSet:forKey:
  • +setSet:forKey:accessibility:
  • +setForKey:

NSDictionary (deprecated)

  • +setDictionary:forKey:
  • +setDictionary:forKey:accessibility:
  • +dictionaryForKey:

NSDate (deprecated)

  • +setDateForKey:
  • +setDateForKey:accessibility:
  • +dateForKey:

All the setXxx methods return BOOL, indicating if the keychain operation succeeded or failed. The xxxForKey methods return a non-nil value on success, or nil on failure.

The setXxx methods will overwrite values for keys that already exist in the keychain, or simply add a keychain entry for the key/value pair if it's not already there.

In all the methods you can use a simple key name, like "MyKey", but know that under the hood Lockbox is prefixing that key with your app's bundle id (if you are using the Class methods, your own key if you are using a Lockbox instance). So the actual key used to store and retrieve the data looks more like "com.mycompany.myapp.MyKey" or "my.custom.key.MyKey". This ensures that your app, and only your app, has access to your data.

The methods with an accessibility argument take a Keychain Item Accessibility Constant. You can use this to control when your keychain item should be readable. For example, passing kSecAttrAccessibleWhenUnlockedThisDeviceOnly will make it accessible only while the device is unlocked, and will not migrate this item to a new device or installation. The methods without a specific accessibility argument will use kSecAttrAccessibleWhenUnlocked, the default in recent iOS versions.

Migrating to Lockbox v3 APIs

There is no automatic migration from the old setXxx methods to the new archive/unarchive methods. In fact, that would be nearly impossible as there is no way to know what you stored for any given key to be able to retrieve the data correctly. The v3 APIs are incompatible with the v2.x APIs.

Manual migration is required. You'll need to fetch your data using the now deprecated type-specific v2.x methods, and then store the data again using the new archiveObjectForKey: method.

You might do this at launch, one time, for all your Lockbox data stored using the old methods. Then make a note by storing another key, either via Lockbox or in User Defaults or somewhere else, that you've done this migration in your app. So on subsequent launches, you wouldn't have to do the migration again. In fact, that would be redundant and probably mess up your data.

ARC Support

As of v2.0, Lockbox is ARC-only. For non-ARC support, use v1.4.9.

Requirements & Limitations

To use this class you will need to add the Security framework to your project.

Your project will have to have Keychain Sharing enabled for Lockbox to access the keychain, but you can remove any Keychain Groups that Xcode adds. The entititlement is apparently required for any keychain access, not just sharing.

This class was written for use under Cocoa Touch and iOS. The code and tests run fine in the iOS simulator under Mac OS. But there are some issues using this class under Cocoa and Mac OS. There are some keychain API differences between the 2 platforms, as it happens. Feel free to fork this repo to make it work for both Cocoa and Cocoa Touch and I'll be happy to consider your pull request!

Note on running unittests on device

If you experience SecItemCopyMatching errors with code 34018 on Lockbox methods while running your app unit tests target on device, your can avoid these by code signing your unit tests .xcttest folder.

Add Run Script phase to your unit tests target Build Phases with:

codesign --verify --force --sign "$CODE_SIGN_IDENTITY" "$CODESIGNING_FOLDER_PATH"

Docs

Link to latest CocoaDocs: cocoadocs.org/docsets/Lockbox/

License

See the LICENSE file for details.

Comments
  • Custom Key Prefix

    Custom Key Prefix

    I've made some changes to allow for a custom key prefix to be specified, defaulting to the bundle id.

    The reason for this is that I am working with iOS app extensions, which have different bundle id's from the main app, but which need access to the keychain of the main app for login credentials.

    The implementation is possibly a bit controversial, as I have changed Lockbox to be instantiable. The first commit simply added a class method, but this seemed unsafe, as setting this after calling some of the setters would mean that the prefix would change, and the previously set key/value pair would be unaccessible. Making the class instantiable means that the prefix has to be set up front, and cannot be changed.

    opened by bravedrake 18
  • SecItemCopyMatching failed for key

    SecItemCopyMatching failed for key

    Somewhy I get this in my console when using Lockbox:

    +[Lockbox objectForKey:] [Line 110] SecItemCopyMatching failed for key com.magin.MaginSpeedMode.udidKey2: -25308 +[Lockbox setObject:forKey:accessibility:] [Line 94] SecItemAdd failed for key com.magin.MaginSpeedMode.udidKey2: -25308

    opened by magin 16
  • Better way to save serialize NSDictionary, NSArray to the Keychain, and also support NSData

    Better way to save serialize NSDictionary, NSArray to the Keychain, and also support NSData

    Instead of using the fixed kDelimeter property list serialization could be used which would be more robust and also provide more complex object trees.

    http://stackoverflow.com/questions/9948698/store-nsdictionary-in-keychain

    opened by rivera-ernesto 8
  • Upgrading from Swift 2.3 -> 3 leaves old values inaccessible.

    Upgrading from Swift 2.3 -> 3 leaves old values inaccessible.

    I'm running into an unfortunate issue after upgrading from Swift 2.3 to Swift 3. I was initially using Lockbox 1.4.9 to store important values in Keychain. Now that I've upgraded to Swift 3 and updated Lockbox's methods (changing stringForKey to string(forKey:) ), I can't seem to access the stored values anymore as they all return nil. They're still in the keychain as I can still access them when I install the Swift 2.3 version of my app.

    What's my best course of action here?

    opened by Menthuss 7
  • LockBox doesn't save info in xcode 8

    LockBox doesn't save info in xcode 8

    [Lockbox archiveObject:self.passwordTextField.text forKey:kPassword]; [Lockbox archiveObject:self.numberTextField.text forKey:kPhone];

    When i try to unarchive it returns nil in simulator, but on the real device it works. In Xcode 7 it works perfect.

    opened by alexeybolv 7
  • Exception thrown when unarchiveObjectForKey called on ios 8

    Exception thrown when unarchiveObjectForKey called on ios 8

    Hi,

    I have notice that because the use of[[NSKeyedUnarchiver alloc] initForReadingWithData:data], It will throw NSInvalidArgumentException [NSKeyedUnarchiver initForReadingWithData:]: incomprehensible archive (xxx,xxx,xxx,0x0, 0x0, 0x0, 0x0, 0x0)

    This only happens on ios8 ... it won't happen on ios9 ... but It think it might be safer to catch the exception within unarchiveObjectForKey.. so ppl using this library won't have unexpected exception happen to them Thanks

    opened by ninjitaru 7
  • Migration to archiveObject

    Migration to archiveObject

    I don't see anything in the readme to guide on migration to the new archiveObject and unarchiveObjectForKey methods, so I assumed it was just straightforward change to the name of the method being called, perhaps with casting as needed. Yet after doing this, I'm running into this error when trying to run my app on a device that had previously used the set* commands of the previous version:

    2016-02-10 16:24:59.504 iATN[3326:539804] *** Terminating app due to uncaught exception   
    'NSInvalidArgumentException', reason: '*** -[NSKeyedUnarchiver initForReadingWithData:]: 
    incomprehensible archive (0x50, 0x57, 0x3d, 0x25, 0x62, 0x61, 0x42, 0x25)'
    *** First throw call stack:
    (0x2b493f8f 0x39b44c8b 0x2b493ed5 0x2c165315 0xac2cc9 0xac3b87 0x16ae90 0x170710 0x16a818 0x16bedc 0x16c21c 0x140514 0x142800 0x2eb810b3 0x2ed77929 0x2ed79fe9 0x2ed84c69 0x2ed7878b 0x3207aec9 0x2b459db5 0x2b459079 0x2b457bb3 0x2b3a3f31 0x2b3a3d43 0x2eb7ac87 0x2eb75879 0x152e84 0x3a0f6aaf)
    libc++abi.dylib: terminating with uncaught exception of type NSException
    

    Is there more to the migration than just changing the methods being called, and casting as appropriate?

    In my case I was doing some array and string storage with the previous version, in a Swift app.

    opened by winzig 5
  • Remove UIKit import

    Remove UIKit import

    A nit-pick perhaps, but this library doesn't utilize any part of UIKit so its inclusion is puzzling and seemingly unnecessary. Please correct me if I'm wrong though.

    opened by nathanhosselton 5
  • Remove

    Remove "key" property from console log.

    @discussion just to be safe, it's not necessary to log the "key" property's value as anyone who profiles your app could potentially get the value by watching the console

    opened by 0xjmp 5
  • Doesn't work under iOS 10

    Doesn't work under iOS 10

    When running version 3.0.2 under iOS 10 I get the error:

    [Lockbox setData:forKey:accessibility:] [Line 167] SecItemAdd failed for key com.xxx: -34018
    

    I also tried running the same code under iOS 9.3.1 and it seems to work fine.

    opened by excitement-engineer 4
  • Is there a way to delete all lockbox keys for my app

    Is there a way to delete all lockbox keys for my app

    Is there a way to delete all lockbox keys? My use case is where the user uninstalls and reinstalls the app? In this case I check a flag in NSUserDefaults ("installed") and if not set I clear the keys.

    opened by byrneciaran 4
  • Lockbox unarchiveObjectForKey: returns nil sometimes

    Lockbox unarchiveObjectForKey: returns nil sometimes

    Hello,

    I use LockBox to store a refresh token and some urls, but sometimes when I call [Lockbox unarchiveObjectForKey:myKey] it returns nil. I don't know why :/

    Here is how I save the data : (the infos came from a WS call)

    [Lockbox archiveObject:oauthDico[@"baseUrl"] forKey:kOAuthBaseUrl]; [Lockbox archiveObject:oauthDico[@"getTokenEndpoint"] forKey:kOAuthTokenEndPoint]; [Lockbox archiveObject:oauthDico[@"refreshTokenEndpoint"] forKey:kOAuthRefreshTokenEndPoint];`

    and this is how I retrieve it :

    NSString* requestUrl = [NSString stringWithFormat:@"%@%@", [Lockbox unarchiveObjectForKey:kOAuthBaseUrl], [Lockbox unarchiveObjectForKey:kOAuthTokenEndPoint]];

    and as I told, sometimes, I didn't figured out when excatly, the unarchive method returns nil, which is very problematic for a login url :p

    Thanks for your help Max

    opened by maxencecornu 2
  • SecItemCopy and SecItemAdd errors

    SecItemCopy and SecItemAdd errors

    Below is a sample code snippet from my program. I originally was trying to store an NSArray in keychain but when I retrieved it it would no longer be an array of GLAAccount *objects, but an array of strings, so I could not access the property on the objects in the array. Now I encode the object's properties into a string and then decode it after I pull it from the keychain using class methods for the object class, in this case GLAAccount. Occasionally, when I pass data to the view controller the below code is in, the saveAccess method is run and I get a secAddItem error and if I load the data from keychain I get a secCopyItemError. I have set the accessibility as kSecAttrAccessibleWhenUnlocked. Any thoughts?

    //save data from self.accounts in keychain
    - (void)saveAccess{
        //clean up the stringArray
        [self.stringArray removeAllObjects];
    
        //encode every account into a string and add it to string array
        for(GLAAccount *account in self.accounts){
            NSString *string = [GLAAccount encodeAccountObject:account];
            [self.stringArray addObject:string];
        }
    
        //save string array into keychain
        NSString *key = [NSString stringWithFormat:@"%@accounts", self.currentUser.username];
        [Lockbox setArray:self.stringArray forKey:key accessibility:kSecAttrAccessibleWhenUnlocked];
    }
    
    opened by RajivRamaiah 1
  • Save data for coredata

    Save data for coredata

    I am working on rss feed based project. I am saving data which entered by user in coredata. is it possible to it also save in lockbox like [Name , websitename , feedurl] and when user uninstall app and install again then data will be copies from lockbox to coredata ?

    Here is my method for save coredata .

              - (void)feedParserDidFinish:(MWFeedParser *)parser {
                     NSFetchRequest *fetchrequestforside=[NSFetchRequest      fetchRequestWithEntityName:@"Sidetablename"]; //Sidetablename is entity
                     fetchrequestforside.predicate=[NSPredicate predicateWithFormat:@"feedurl = %@", self.Feedlink];
                  NSManagedObjectContext *context = [self managedObjectContext];
                 if ([[context executeFetchRequest:fetchrequestforside error:NULL] count] == 0) {
                    // Create a new managed object
    
                    NSError *error = nil;
    
                    NSPredicate *predicate=[NSPredicate predicateWithFormat:@"feedurl contains %@",check ]; //check is urlstring
                    [fetchrequestforside setPredicate:predicate];
                    NSMutableArray *checkp = [[context executeFetchRequest:fetchrequestforside error:&error]mutableCopy];
    
    
                    if (checkp.count<=0) //if coredata will find url then notstore in coredata else storeprocedure
          {
    
                    NSManagedObject *newDevice1 = [NSEntityDescription insertNewObjectForEntityForName:@"Sidetablename" inManagedObjectContext:context];
    
                    if (self.Name)
                    {
                        [newDevice1 setValue:self.Name forKey:@"name"];
    
    
                    }
                    if (self.WebsiteName)
                    {
                        [newDevice1 setValue:self.WebsiteName forKey:@"websitename"];
    
                    }
    
                    if (self.Feedlink)
                    {
                        [newDevice1 setValue:self.Feedlink forKey:@"feedurl"];
    
                    }
    
    
    
    
                    NSError *error = nil;
                    if (![context save:&error]) {
                            NSLog(@"Can't Save! %@ %@", error, [error localizedDescription]); // Getting error here through nslog //CoreData: error: (1555) UNIQUE constraint failed: ZSIDETABLENAME.Z_PK
    
                        }
    
    opened by badalpub1991 6
  • Occasional Nil value for String.

    Occasional Nil value for String.

    At the moment I am using Lockbox to store a sessionToken in keychain.

    Occasionally Lockbox returns a nil value when returning the sessionToken string.

    Should i be returning the value from Lockbox every time I make a network request, or should I be assigning it to a property within the application when it's first retrieved?

    Is there an issue with getting data stored in keychain?

    opened by ghost 3
  • Feature Request: Add support for iCloud keychain?

    Feature Request: Add support for iCloud keychain?

    Believe in iOS7.01, Apple allowed you to store the keychain in iCloud if the user has it enabled. I think all you need to do is add kSecAttrSynchronizable in certain areas.

    opened by kissfro 2
Owner
Mark Granoff
Mark Granoff
Simple Objective-C wrapper for the keychain that works on Mac and iOS

SAMKeychain SAMKeychain is a simple wrapper for accessing accounts, getting passwords, setting passwords, and deleting passwords using the system Keyc

Sam Soffes 5.4k Jan 8, 2023
RSA public/private key encryption, private key signing and public key verification in Swift using the Swift Package Manager. Works on iOS, macOS, and Linux (work in progress).

BlueRSA Swift cross-platform RSA wrapper library for RSA encryption and signing. Works on supported Apple platforms (using Security framework). Linux

Kitura 122 Dec 16, 2022
RSA public/private key encryption, private key signing and public key verification in Swift using the Swift Package Manager. Works on iOS, macOS, and Linux (work in progress).

BlueRSA Swift cross-platform RSA wrapper library for RSA encryption and signing. Works on supported Apple platforms (using Security framework). Linux

Kitura 122 Dec 16, 2022
A key value store for storing per-developer environment and application keys

A key value store for enviroment and application keys. Its good security practice to keep production keys out of developer hands. CocoaPods-keys makes

Orta Therox 1.5k Dec 20, 2022
JSPatch bridge Objective-C and Javascript using the Objective-C runtime. You can call any Objective-C class and method in JavaScript by just including a small engine. JSPatch is generally used to hotfix iOS App.

JSPatch 中文介绍 | 文档 | JSPatch平台 请大家不要自行接入 JSPatch,统一接入 JSPatch 平台,让热修复在一个安全和可控的环境下使用。原因详见 这里 JSPatch bridges Objective-C and JavaScript using the Object

bang 11.4k Jan 1, 2023
Valet lets you securely store data in the iOS, tvOS, or macOS Keychain without knowing a thing about how the Keychain works.

Valet Valet lets you securely store data in the iOS, tvOS, watchOS, or macOS Keychain without knowing a thing about how the Keychain works. It’s easy.

Square 3.8k Jan 4, 2023
Valet lets you securely store data in the iOS, tvOS, or macOS Keychain without knowing a thing about how the Keychain works. It’s easy. We promise.

Valet Valet lets you securely store data in the iOS, tvOS, watchOS, or macOS Keychain without knowing a thing about how the Keychain works. It’s easy.

Square 3.8k Jan 4, 2023
⛓ Easy to Read and Write Multi-chain Animations Lib in Objective-C and Swift.

中文介绍 This project is inspired by JHChainableAnimations! Why Choose LSAnimator & CoreAnimator? You can write complex and easy-to-maintain animations in

木子 1.6k Nov 22, 2022
COVID Certificate is the official app for storing and presenting COVID certificates issued in Switzerland.

COVID Certificate is the official app for storing and presenting COVID certificates issued in Switzerland. The certificates are kept and checked locally on the user's phone.

Swiss Admin 111 Dec 19, 2022
Secretive is an app for storing and managing SSH keys in the Secure Enclave.

Secretive is an app for storing and managing SSH keys in the Secure Enclave.

Max Goedjen 4.9k Jan 1, 2023
A trivial app for storing and viewing famous quotes

Paraphrase A trivial app for storing and viewing famous quotes This app was specifically designed to accompany a tutorial series about modern app infr

Leopold Lemmermann 0 Dec 15, 2021
Helper functions for saving text in Keychain securely for iOS, OS X, tvOS and watchOS.

Helper functions for storing text in Keychain for iOS, macOS, tvOS and WatchOS This is a collection of helper functions for saving text and data in th

Evgenii Neumerzhitckii 2.3k Dec 28, 2022
Securely synchronize any CareKit 2.1+ based app to a Parse Server Cloud. Compatible with parse-hipaa.

ParseCareKit Use at your own risk. There is no promise that this is HIPAA compliant and we are not responsible for any mishandling of your data This f

Network Reconnaissance Lab 31 Nov 24, 2022
Appfiguratesdk - Appfigurate provides the ability to change configuration properties in iOS and watchOS, apps and app extensions, securely, at runtime.

Appfigurate™ Appfigurate provides the ability to change configuration properties in iOS and watchOS, apps and app extensions, securely, at runtime. Do

Electric Bolt 21 Dec 14, 2022
A wrapper to make it really easy to deal with iOS, macOS, watchOS and Linux Keychain and store your user's credentials securely.

A wrapper (written only in Swift) to make it really easy to deal with iOS, macOS, watchOS and Linux Keychain and store your user's credentials securely.

Ezequiel Aceto 2 Mar 29, 2022
Modern thread-safe and type-safe key-value observing for Swift and Objective-C

Now Archived and Forked PMKVObserver will not be maintained in this repository going forward. Please use, create issues on, and make PRs to the fork o

Postmates Inc. 708 Jun 29, 2022
YapDB is a collection/key/value store with a plugin architecture. It's built atop sqlite, for Swift & objective-c developers.

YapDatabase is a collection/key/value store and so much more. It's built atop sqlite, for Swift & Objective-C developers, targeting macOS, iOS, tvOS &

Yap Studios 3.3k Dec 29, 2022
An Objective-C wrapper for RocksDB - A Persistent Key-Value Store for Flash and RAM Storage.

ObjectiveRocks ObjectiveRocks is an Objective-C wrapper of Facebook's RocksDB - A Persistent Key-Value Store for Flash and RAM Storage. Current RocksD

Iskandar Abudiab 56 Nov 5, 2022
A fast, convenient and nonintrusive conversion framework between JSON and model. Your model class doesn't need to extend any base class. You don't need to modify any model file.

MJExtension A fast, convenient and nonintrusive conversion framework between JSON and model. 转换速度快、使用简单方便的字典转模型框架 ?? ✍??Release Notes: more details Co

M了个J 8.5k Jan 3, 2023
A simple class that wraps the process of saving or loading a struct or class into a single file

EZFile This is a simple class that wraps the process of saving or loading a stru

null 7 May 16, 2022