- Upgrade to 1.0
This commit is contained in:
1 parent
4e66c377fd
commit
0c2b3f1c25
452 files changed
+33888
-16806
No files matched your search
Generated
+19
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2014 axldyb <aksel.dybdal@shortcut.no>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -0,0 +1,46 @@
|
||||
//
|
||||
// DDDKeychainWrapper.h
|
||||
// Aksel Dybdal
|
||||
//
|
||||
// Created by Aksel Dybdal on 02.04.14.
|
||||
// Copyright (c) 2014 Aksel Dybdal. All rights reserved.
|
||||
//
|
||||
|
||||
/**
|
||||
A small wrapper for Keychain access.
|
||||
|
||||
Inspired by:
|
||||
http://useyourloaf.com/blog/2010/03/29/simple-iphone-keychain-access.html
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface DDDKeychainWrapper : NSObject
|
||||
|
||||
+ (void)setString:(NSString *)string forKey:(NSString *)key;
|
||||
+ (NSString *)stringForKey:(NSString *)key;
|
||||
|
||||
+ (void)setDate:(NSDate *)date forKey:(NSString *)key;
|
||||
+ (NSDate *)dateForKey:(NSString *)key;
|
||||
|
||||
+ (void)setData:(NSData *)data forKey:(NSString *)key;
|
||||
+ (NSData *)dataForKey:(NSString *)key;
|
||||
|
||||
+ (void)setArray:(NSArray *)array forKey:(NSString *)key;
|
||||
+ (NSArray *)arrayForKey:(NSString *)key;
|
||||
|
||||
+ (void)setDictionary:(NSDictionary *)dictionary forKey:(NSString *)key;
|
||||
+ (NSDictionary *)dictionaryForKey:(NSString *)key;
|
||||
|
||||
+ (void)setNumber:(NSNumber *)number forKey:(NSString *)key;
|
||||
+ (NSNumber *)numberForKey:(NSString *)key;
|
||||
|
||||
+ (void)setBoolean:(BOOL)boolean forKey:(NSString *)key;
|
||||
+ (BOOL)booleanForKey:(NSString *)key;
|
||||
|
||||
+ (void)setObject:(id)object forKey:(NSString *)key;
|
||||
+ (id)objectForKey:(NSString *)key;
|
||||
|
||||
+ (void)wipeKeychain;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,373 @@
|
||||
//
|
||||
// DDDKeychainWrapper.m
|
||||
// Aksel Dybdal
|
||||
//
|
||||
// Created by Aksel Dybdal on 02.04.14.
|
||||
// Copyright (c) 2014 Aksel Dybdal. All rights reserved.
|
||||
//
|
||||
|
||||
#import "DDDKeychainWrapper.h"
|
||||
#import <Security/Security.h>
|
||||
|
||||
typedef NS_ENUM(NSUInteger, DDDKeychainWrapperErrorCode) {
|
||||
DDDKeychainWrapperErrorCreatingKeychainValue = 1,
|
||||
DDDKeychainWrapperErrorUpdatingKeychainValue,
|
||||
DDDKeychainWrapperErrorDeletingKeychainValue,
|
||||
DDDKeychainWrapperErrorSearchingKeychainValue
|
||||
};
|
||||
|
||||
NSString *const kDDDKeychainWrapperServiceName = @"com.dddkeychainwrapper.keychainService";
|
||||
NSString *const kDDDKeychainWrapperErrorDomain = @"DDDKeychainWrapperErrorDomain";
|
||||
|
||||
#ifdef DEBUG
|
||||
# warning "Including NSLog"
|
||||
# define DDDLOG(s, ...) NSLog(s, ## __VA_ARGS__)
|
||||
#else
|
||||
# define DDDLOG(s, ...) while(0){}
|
||||
#endif
|
||||
|
||||
@implementation DDDKeychainWrapper
|
||||
|
||||
|
||||
#pragma mark - String
|
||||
|
||||
+ (void)setString:(NSString *)string forKey:(NSString *)key
|
||||
{
|
||||
NSData *stringData = [string dataUsingEncoding:NSUTF8StringEncoding];
|
||||
[self setData:stringData forIdentifier:key];
|
||||
}
|
||||
|
||||
+ (NSString *)stringForKey:(NSString *)key
|
||||
{
|
||||
NSData *stringData = [self dataForIdentifier:key];
|
||||
return [[NSString alloc] initWithData:stringData encoding:NSUTF8StringEncoding];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Date
|
||||
|
||||
+ (void)setDate:(NSDate *)date forKey:(NSString *)key
|
||||
{
|
||||
NSData *dateData = [NSKeyedArchiver archivedDataWithRootObject:date];
|
||||
[self setData:dateData forIdentifier:key];
|
||||
}
|
||||
|
||||
+ (NSDate *)dateForKey:(NSString *)key
|
||||
{
|
||||
NSData *dateData = [self dataForIdentifier:key];
|
||||
return (NSDate *)[NSKeyedUnarchiver unarchiveObjectWithData:dateData];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Data
|
||||
|
||||
+ (void)setData:(NSData *)data forKey:(NSString *)key
|
||||
{
|
||||
[self setData:data forIdentifier:key];
|
||||
}
|
||||
|
||||
+ (NSData *)dataForKey:(NSString *)key
|
||||
{
|
||||
return [self dataForIdentifier:key];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Array
|
||||
|
||||
+ (void)setArray:(NSArray *)array forKey:(NSString *)key
|
||||
{
|
||||
for (id obj in array) {
|
||||
NSAssert([obj conformsToProtocol:@protocol(NSCoding)], @"Objects must confirm to NSCoding protocol in order to be stored in keychain");
|
||||
}
|
||||
|
||||
NSData *arrayData = [NSKeyedArchiver archivedDataWithRootObject:array];
|
||||
[self setData:arrayData forIdentifier:key];
|
||||
}
|
||||
|
||||
+ (NSArray *)arrayForKey:(NSString *)key
|
||||
{
|
||||
NSData *arrayData = [self dataForIdentifier:key];
|
||||
return (NSArray *)[NSKeyedUnarchiver unarchiveObjectWithData:arrayData];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Dictionary
|
||||
|
||||
+ (void)setDictionary:(NSDictionary *)dictionary forKey:(NSString *)key
|
||||
{
|
||||
[dictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
|
||||
NSAssert([key conformsToProtocol:@protocol(NSCoding)], @"Keys must confirm to NSCoding protocol in order to be stored in keychain");
|
||||
NSAssert([obj conformsToProtocol:@protocol(NSCoding)], @"Objects must confirm to NSCoding protocol in order to be stored in keychain");
|
||||
}];
|
||||
|
||||
NSData *dictionaryData = [NSKeyedArchiver archivedDataWithRootObject:dictionary];
|
||||
[self setData:dictionaryData forIdentifier:key];
|
||||
}
|
||||
|
||||
+ (NSDictionary *)dictionaryForKey:(NSString *)key
|
||||
{
|
||||
NSData *dictionaryData = [self dataForIdentifier:key];
|
||||
return (NSDictionary *)[NSKeyedUnarchiver unarchiveObjectWithData:dictionaryData];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Number
|
||||
|
||||
+ (void)setNumber:(NSNumber *)number forKey:(NSString *)key
|
||||
{
|
||||
NSData *numberData = [NSKeyedArchiver archivedDataWithRootObject:number];
|
||||
[self setData:numberData forIdentifier:key];
|
||||
}
|
||||
|
||||
+ (NSNumber *)numberForKey:(NSString *)key
|
||||
{
|
||||
NSData *numberData = [self dataForIdentifier:key];
|
||||
return (NSNumber *)[NSKeyedUnarchiver unarchiveObjectWithData:numberData];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - BOOL
|
||||
|
||||
+ (void)setBoolean:(BOOL)boolean forKey:(NSString *)key
|
||||
{
|
||||
NSNumber *boolNumber = [NSNumber numberWithBool:boolean];
|
||||
[self setNumber:boolNumber forKey:key];
|
||||
}
|
||||
|
||||
+ (BOOL)booleanForKey:(NSString *)key
|
||||
{
|
||||
NSNumber *boolNumber = [self numberForKey:key];
|
||||
return [boolNumber boolValue];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Object
|
||||
|
||||
+ (void)setObject:(id)object forKey:(NSString *)key
|
||||
{
|
||||
NSAssert([object conformsToProtocol:@protocol(NSCoding)], @"Object must confirm to NSCoding protocol in order to be stored in keychain");
|
||||
|
||||
NSData *objectData = [NSKeyedArchiver archivedDataWithRootObject:object];
|
||||
[self setData:objectData forIdentifier:key];
|
||||
}
|
||||
|
||||
+ (id)objectForKey:(NSString *)key
|
||||
{
|
||||
NSData *objectData = [self dataForIdentifier:key];
|
||||
return (id)[NSKeyedUnarchiver unarchiveObjectWithData:objectData];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Clear Keychain
|
||||
|
||||
+ (void)wipeKeychain
|
||||
{
|
||||
NSArray *secItemClasses = @[(__bridge id)kSecClassGenericPassword,
|
||||
(__bridge id)kSecClassInternetPassword,
|
||||
(__bridge id)kSecClassCertificate,
|
||||
(__bridge id)kSecClassKey,
|
||||
(__bridge id)kSecClassIdentity];
|
||||
|
||||
for (id secItemClass in secItemClasses) {
|
||||
NSDictionary *spec = @{(__bridge id)kSecClass: (id)secItemClass};
|
||||
SecItemDelete((__bridge CFDictionaryRef)spec);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
+ (void)setData:(NSData *)data forIdentifier:(NSString *)identifier
|
||||
{
|
||||
NSError *error = nil;
|
||||
|
||||
// If no data provided we assume we want to delete the value
|
||||
if (nil == data || NO == [data bytes]) {
|
||||
[self deleteKeychainValueForIdentifier:identifier error:&error];
|
||||
if (error) {
|
||||
DDDLOG(@"Error deleting keychain value for key: \"%@\" Error: %@", identifier, [error localizedDescription]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// We first look up the key in order to see if we need to update or create the value
|
||||
if ([self searchKeychainCopyMatching:identifier error:&error]) {
|
||||
if (error) {
|
||||
DDDLOG(@"Error finding keychain value for key: \"%@\" Error: %@", identifier, [error localizedDescription]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (![self updateKeychainValue:data forIdentifier:identifier error:&error]) {
|
||||
DDDLOG(@"Error updating keychain value for key: \"%@\" Error: %@", identifier, [error localizedDescription]);
|
||||
}
|
||||
} else {
|
||||
if (![self createKeychainValue:data forIdentifier:identifier error:&error]) {
|
||||
DDDLOG(@"Error creating keychain value for key: \"%@\" Error: %@", identifier, [error localizedDescription]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+ (NSData *)dataForIdentifier:(NSString *)identifier
|
||||
{
|
||||
NSError *error = nil;
|
||||
NSData *stringData = [self searchKeychainCopyMatching:identifier error:&error];
|
||||
if (error) {
|
||||
DDDLOG(@"Error finding keychain value for key: \"%@\" Error: %@", identifier, [error localizedDescription]);
|
||||
return nil;
|
||||
}
|
||||
return stringData;
|
||||
}
|
||||
|
||||
+ (NSMutableDictionary *)newSearchDictionary:(NSString *)identifier
|
||||
{
|
||||
NSMutableDictionary *searchDictionary = [[NSMutableDictionary alloc] init];
|
||||
|
||||
[searchDictionary setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
|
||||
|
||||
NSData *encodedIdentifier = [identifier dataUsingEncoding:NSUTF8StringEncoding];
|
||||
[searchDictionary setObject:encodedIdentifier forKey:(__bridge id)kSecAttrGeneric];
|
||||
[searchDictionary setObject:encodedIdentifier forKey:(__bridge id)kSecAttrAccount];
|
||||
[searchDictionary setObject:(__bridge id)kSecAttrAccessibleAfterFirstUnlock forKey:(__bridge id)kSecAttrAccessible];
|
||||
[searchDictionary setObject:kDDDKeychainWrapperServiceName forKey:(__bridge id)kSecAttrService];
|
||||
|
||||
return searchDictionary;
|
||||
}
|
||||
|
||||
+ (NSData *)searchKeychainCopyMatching:(NSString *)identifier error:(NSError **)error
|
||||
{
|
||||
NSMutableDictionary *searchDictionary = [self newSearchDictionary:identifier];
|
||||
|
||||
[searchDictionary setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit];
|
||||
[searchDictionary setObject:(__bridge id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData];
|
||||
|
||||
CFDataRef result;
|
||||
OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)searchDictionary, (CFTypeRef *)&result);
|
||||
|
||||
if (status != errSecSuccess) {
|
||||
[self keychainError:error forStatus:status domain:DDDKeychainWrapperErrorSearchingKeychainValue];
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSData *data = (__bridge NSData *)result;
|
||||
return data;
|
||||
}
|
||||
|
||||
+ (BOOL)createKeychainValue:(NSData *)data forIdentifier:(NSString *)identifier error:(NSError **)error
|
||||
{
|
||||
NSMutableDictionary *dictionary = [self newSearchDictionary:identifier];
|
||||
[dictionary setObject:data forKey:(__bridge id)kSecValueData];
|
||||
|
||||
OSStatus status = SecItemAdd((__bridge CFDictionaryRef)dictionary, NULL);
|
||||
|
||||
if (status == errSecSuccess) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
[self keychainError:error forStatus:status domain:DDDKeychainWrapperErrorCreatingKeychainValue];
|
||||
return NO;
|
||||
}
|
||||
|
||||
+ (BOOL)updateKeychainValue:(NSData *)data forIdentifier:(NSString *)identifier error:(NSError **)error
|
||||
{
|
||||
NSMutableDictionary *searchDictionary = [self newSearchDictionary:identifier];
|
||||
NSMutableDictionary *updateDictionary = [[NSMutableDictionary alloc] init];
|
||||
[updateDictionary setObject:data forKey:(__bridge id)kSecValueData];
|
||||
|
||||
OSStatus status = SecItemUpdate((__bridge CFDictionaryRef)searchDictionary,
|
||||
(__bridge CFDictionaryRef)updateDictionary);
|
||||
|
||||
if (status == errSecSuccess) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
[self keychainError:error forStatus:status domain:DDDKeychainWrapperErrorUpdatingKeychainValue];
|
||||
return NO;
|
||||
}
|
||||
|
||||
+ (BOOL)deleteKeychainValueForIdentifier:(NSString *)identifier error:(NSError **)error
|
||||
{
|
||||
NSMutableDictionary *searchDictionary = [self newSearchDictionary:identifier];
|
||||
OSStatus status = SecItemDelete((__bridge CFDictionaryRef)searchDictionary);
|
||||
|
||||
if (status == errSecSuccess) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
[self keychainError:error forStatus:status domain:DDDKeychainWrapperErrorDeletingKeychainValue];
|
||||
return NO;
|
||||
}
|
||||
|
||||
#pragma mark - Error
|
||||
|
||||
+ (void)keychainError:(NSError **)error forStatus:(OSStatus)status domain:(NSUInteger)domain
|
||||
{
|
||||
NSString *errorString = @"";
|
||||
|
||||
switch (status) {
|
||||
case errSecUnimplemented:
|
||||
errorString = @"Function or operation not implemented.";
|
||||
break;
|
||||
|
||||
case errSecIO:
|
||||
errorString = @"I/O error (bummers)";
|
||||
break;
|
||||
|
||||
case errSecOpWr:
|
||||
errorString = @"File already open with write permission";
|
||||
break;
|
||||
|
||||
case errSecParam:
|
||||
errorString = @"One or more parameters passed to a function where not valid.";
|
||||
break;
|
||||
|
||||
case errSecAllocate:
|
||||
errorString = @"Failed to allocate memory.";
|
||||
break;
|
||||
|
||||
case errSecUserCanceled:
|
||||
errorString = @"User canceled the operation.";
|
||||
break;
|
||||
|
||||
case errSecBadReq:
|
||||
errorString = @"Bad parameter or invalid state for operation.";
|
||||
break;
|
||||
|
||||
case errSecInternalComponent:
|
||||
errorString = @"errSecInternalComponent";
|
||||
break;
|
||||
|
||||
case errSecNotAvailable:
|
||||
errorString = @"No keychain is available. You may need to restart your computer.";
|
||||
break;
|
||||
|
||||
case errSecDuplicateItem:
|
||||
errorString = @"The specified item already exists in the keychain.";
|
||||
break;
|
||||
|
||||
case errSecItemNotFound:
|
||||
errorString = @"The specified item could not be found in the keychain.";
|
||||
break;
|
||||
|
||||
case errSecInteractionNotAllowed:
|
||||
errorString = @"User interaction is not allowed.";
|
||||
break;
|
||||
|
||||
case errSecDecode:
|
||||
errorString = @"Unable to decode the provided data.";
|
||||
break;
|
||||
|
||||
case errSecAuthFailed:
|
||||
errorString = @"The user name or passphrase you entered is not correct.";
|
||||
break;
|
||||
|
||||
default:
|
||||
errorString = @"Unknown error";
|
||||
break;
|
||||
}
|
||||
|
||||
*error = [NSError errorWithDomain:kDDDKeychainWrapperErrorDomain
|
||||
code:domain
|
||||
userInfo:@{NSLocalizedDescriptionKey: errorString}];
|
||||
}
|
||||
|
||||
@end
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
# DDDKeychainWrapper
|
||||
|
||||
[](https://travis-ci.org/axldyb/DDDKeychainWrapper)
|
||||
[](http://cocoadocs.org/docsets/DDDKeychainWrapper)
|
||||
[](http://cocoadocs.org/docsets/DDDKeychainWrapper)
|
||||
[](http://cocoadocs.org/docsets/DDDKeychainWrapper)
|
||||
|
||||
## Usage
|
||||
|
||||
Storing your sensitive data in the keychain can take up a lot of time and effort. It should be easy to just drop something in there and retrive it with a few simple lines of code. DDDKeychainWrapper offers this simplicity and here is how we do it:
|
||||
|
||||
```objective-c
|
||||
// Writing to the Keychain
|
||||
[DDDKeychainWrapper setString:@"Secret string" forKey:@"my_key"];
|
||||
|
||||
// Reading from the Keychain
|
||||
NSString *secretString = [DDDKeychainWrapper stringForKey:@"my_key"];
|
||||
```
|
||||
|
||||
DDDKeychainWrapper has support for the following types
|
||||
|
||||
* NSString
|
||||
* NSDate
|
||||
* NSData
|
||||
* NSArray
|
||||
* NSDictionary
|
||||
* NSNumber
|
||||
* BOOL
|
||||
* Objects (Must confirm to NSCoding)
|
||||
|
||||
A method for wiping the keychain data inserted via the wrapper exists as well.
|
||||
|
||||
## Installation
|
||||
|
||||
DDDKeychainWrapper is available through [CocoaPods](http://cocoapods.org). To install
|
||||
it, simply add the following line to your Podfile:
|
||||
|
||||
pod "DDDKeychainWrapper"
|
||||
|
||||
## Author
|
||||
|
||||
axldyb, aksel.dybdal@shortcut.no
|
||||
|
||||
## License
|
||||
|
||||
DDDKeychainWrapper is available under the MIT license. See the LICENSE file for more info.
|
||||
|
||||
Reference in new issue
Block a user