5 // Created by Jens Alfke on 3/23/09.
6 // Copyright 2009 Jens Alfke. All rights reserved.
10 #import "MYCrypto_Private.h"
12 #import "MYIdentity.h"
15 #if !MYCRYPTO_USE_IPHONE_API
18 @interface MYKeyEnumerator : NSEnumerator
21 MYKeychain *_keychain;
22 SecKeychainSearchRef _search;
23 SecItemClass _itemClass;
26 - (id) initWithKeychain: (MYKeychain*)keychain
27 itemClass: (SecItemClass)itemClass
28 attributes: (SecKeychainAttribute[])attributes
29 count: (unsigned)count;
33 @interface MYIdentityEnumerator : NSEnumerator
36 SecIdentitySearchRef _searchRef;
39 - (id) initWithKeychain: (MYKeychain*)keychain;
45 @implementation MYKeychain
48 - (id) initWithKeychainRef: (SecKeychainRef)keychainRef
53 CFRetain(keychainRef);
54 _keychain = keychainRef;
60 + (MYKeychain*) _readableKeychainWithRef: (SecKeychainRef)keychainRef fromPath: (NSString*)path {
63 SecKeychainStatus status;
64 BOOL ok = check(SecKeychainGetStatus(keychainRef, &status), @"SecKeychainGetStatus");
65 if (ok && !(status & kSecReadPermStatus)) {
66 Warn(@"Can't open keychain at %@ : not readable (status=%i)", path,status);
69 MYKeychain *keychain = nil;
71 keychain = [[[self alloc] initWithKeychainRef: keychainRef] autorelease];
72 CFRelease(keychainRef);
76 + (MYKeychain*) openKeychainAtPath: (NSString*)path
79 SecKeychainRef keychainRef = NULL;
80 if (!check(SecKeychainOpen(path.fileSystemRepresentation, &keychainRef), @"SecKeychainOpen"))
82 return [self _readableKeychainWithRef: keychainRef fromPath: path];
85 + (MYKeychain*) createKeychainAtPath: (NSString*)path
86 withPassword: (NSString*)password
89 const char *passwordStr = [password UTF8String];
90 SecKeychainRef keychainRef = NULL;
91 if (!check(SecKeychainCreate(path.fileSystemRepresentation,
92 passwordStr ?strlen(passwordStr) :0,
97 @"SecKeychainCreate"))
99 return [self _readableKeychainWithRef: keychainRef fromPath: path];
102 - (BOOL) deleteKeychainFile {
104 return check(SecKeychainDelete(_keychain), @"SecKeychainDelete");
110 if (_keychain) CFRelease(_keychain);
116 if (_keychain) CFRelease(_keychain);
121 + (MYKeychain*) allKeychains
123 static MYKeychain *sAllKeychains;
124 @synchronized(self) {
126 sAllKeychains = [[self alloc] initWithKeychainRef: nil];
128 return sAllKeychains;
132 + (MYKeychain*) defaultKeychain
134 static MYKeychain *sDefaultKeychain;
135 @synchronized(self) {
136 if (!sDefaultKeychain) {
137 SecKeychainRef kc = NULL;
138 OSStatus err = SecKeychainCopyDomainDefault(kSecPreferencesDomainUser,&kc);
140 // In the simulator, an app is run in a sandbox that has no keychain by default.
141 // As a convenience, create one if necessary:
142 if (err == errSecNoDefaultKeychain) {
143 Log(@"No default keychain in simulator; creating one...");
144 NSString *path = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,
145 NSUserDomainMask, YES) objectAtIndex: 0];
146 path = [path stringByAppendingPathComponent: @"MYCrypto.keychain"];
147 sDefaultKeychain = [[self createKeychainAtPath: path withPassword: nil] retain];
148 Assert(sDefaultKeychain, @"Couldn't create default keychain");
149 SecKeychainSetDomainDefault(kSecPreferencesDomainUser, sDefaultKeychain.keychainRef);
150 Log(@"...created %@", sDefaultKeychain);
151 return sDefaultKeychain;
154 if (!check(err, @"SecKeychainCopyDefault"))
157 Assert(kc, @"No default keychain");
158 sDefaultKeychain = [[self alloc] initWithKeychainRef: kc];
162 return sDefaultKeychain;
166 - (id) copyWithZone: (NSZone*)zone {
167 // It's not necessary to make copies of Keychain objects. This makes it more efficient
168 // to use instances as NSDictionary keys or store them in NSSets.
169 return [self retain];
172 - (BOOL) isEqual: (id)obj {
173 return (obj == self) ||
174 ([obj isKindOfClass: [MYKeychain class]] && CFEqual(_keychain, [obj keychainRef]));
178 - (SecKeychainRef) keychainRef {
183 - (SecKeychainRef) keychainRefOrDefault {
187 return [[[self class] defaultKeychain] keychainRef];
194 char pathBuf[PATH_MAX];
195 UInt32 pathLen = sizeof(pathBuf);
196 if (!check(SecKeychainGetPath(_keychain, &pathLen, pathBuf), @"SecKeychainGetPath"))
198 return [[NSFileManager defaultManager] stringWithFileSystemRepresentation: pathBuf length: pathLen];
201 - (NSString*) description {
203 return $sprintf(@"%@[%p, %@]", [self class], _keychain, self.path);
205 return $sprintf(@"%@[all]", [self class]);
210 #pragma mark SEARCHING:
213 - (MYKeychainItem*) itemOfClass: (SecItemClass)itemClass
214 withDigest: (MYSHA1Digest*)pubKeyDigest
216 SecKeychainAttribute attr = {.tag= (itemClass==kSecCertificateItemClass ?kSecPublicKeyHashItemAttr :kSecKeyLabel),
217 .length= pubKeyDigest.length,
218 .data= (void*) pubKeyDigest.bytes};
219 MYKeyEnumerator *e = [[MYKeyEnumerator alloc] initWithKeychain: self
221 attributes: &attr count: 1];
222 MYKeychainItem *item = e.nextObject;
227 - (MYPublicKey*) publicKeyWithDigest: (MYSHA1Digest*)pubKeyDigest {
228 return (MYPublicKey*) [self itemOfClass: kSecPublicKeyItemClass withDigest: pubKeyDigest];
231 - (NSEnumerator*) enumeratePublicKeys {
232 return [[[MYKeyEnumerator alloc] initWithKeychain: self
233 itemClass: kSecPublicKeyItemClass
234 attributes: NULL count: 0] autorelease];
237 - (NSEnumerator*) publicKeysWithAlias: (NSString*)alias {
238 NSData *utf8 = [alias dataUsingEncoding: NSUTF8StringEncoding];
239 SecKeychainAttribute attr = {.tag=kSecKeyAlias, .length=utf8.length, .data=(void*)utf8.bytes};
240 return [[[MYKeyEnumerator alloc] initWithKeychain: self
241 itemClass: kSecPublicKeyItemClass
242 attributes: &attr count: 1] autorelease];
246 - (MYPrivateKey*) privateKeyWithDigest: (MYSHA1Digest*)pubKeyDigest {
247 return (MYPrivateKey*) [self itemOfClass: kSecPrivateKeyItemClass withDigest: pubKeyDigest];
250 - (NSEnumerator*) enumeratePrivateKeys {
251 return [[[MYKeyEnumerator alloc] initWithKeychain: self
252 itemClass: kSecPrivateKeyItemClass
253 attributes: NULL count: 0] autorelease];
256 - (MYCertificate*) certificateWithDigest: (MYSHA1Digest*)pubKeyDigest {
257 return (MYCertificate*) [self itemOfClass: kSecCertificateItemClass withDigest: pubKeyDigest];
260 - (NSEnumerator*) enumerateCertificates {
261 return [[[MYKeyEnumerator alloc] initWithKeychain: self
262 itemClass: kSecCertificateItemClass
263 attributes: NULL count: 0] autorelease];
266 - (NSEnumerator*) enumerateIdentities {
267 return [[[MYIdentityEnumerator alloc] initWithKeychain: self] autorelease];
270 - (NSEnumerator*) enumerateSymmetricKeys {
271 return [[[MYKeyEnumerator alloc] initWithKeychain: self
272 itemClass: kSecSymmetricKeyItemClass
273 attributes: NULL count: 0] autorelease];
276 - (NSEnumerator*) symmetricKeysWithAlias: (NSString*)alias {
277 NSData *utf8 = [alias dataUsingEncoding: NSUTF8StringEncoding];
278 SecKeychainAttribute attr = {.tag=kSecKeyAlias, .length=utf8.length, .data=(void*)utf8.bytes};
279 return [[[MYKeyEnumerator alloc] initWithKeychain: self
280 itemClass: kSecSymmetricKeyItemClass
281 attributes: &attr count: 1] autorelease];
290 - (MYPublicKey*) importPublicKey: (NSData*)keyData {
291 return [[[MYPublicKey alloc] _initWithKeyData: keyData
292 forKeychain: self.keychainRefOrDefault]
296 - (MYPrivateKey*) importPublicKey: (NSData*)pubKeyData
297 privateKey: (NSData*)privKeyData
298 alertTitle: (NSString*)title
299 alertPrompt: (NSString*)prompt {
300 return [[[MYPrivateKey alloc] _initWithKeyData: privKeyData
301 publicKeyData: pubKeyData
302 forKeychain: self.keychainRefOrDefault
303 alertTitle: (NSString*)title
304 alertPrompt: (NSString*)prompt]
308 - (MYPrivateKey*) importPublicKey: (NSData*)pubKeyData
309 privateKey: (NSData*)privKeyData
311 return [self importPublicKey: pubKeyData privateKey: privKeyData
312 alertTitle: @"Import Private Key"
313 alertPrompt: @"To import your saved private key, please re-enter the "
314 "passphrase you used when you exported it."];
317 - (MYCertificate*) importCertificate: (NSData*)data
318 type: (CSSM_CERT_TYPE) type
319 encoding: (CSSM_CERT_ENCODING) encoding;
321 MYCertificate *cert = [[[MYCertificate alloc] initWithCertificateData: data
326 if (!check(SecCertificateAddToKeychain(cert.certificateRef, self.keychainRefOrDefault),
327 @"SecCertificateAddToKeychain"))
333 - (MYCertificate*) importCertificate: (NSData*)data {
334 return [self importCertificate: data
335 type: CSSM_CERT_X_509v3
336 encoding: CSSM_CERT_ENCODING_BER];
339 - (BOOL) addCertificate: (MYCertificate*)certificate {
341 return check(SecCertificateAddToKeychain(certificate.certificateRef, self.keychainRefOrDefault),
342 @"SecCertificateAddToKeychain");
346 - (MYSymmetricKey*) generateSymmetricKeyOfSize: (unsigned)keySizeInBits
347 algorithm: (CCAlgorithm)algorithm
349 return [MYSymmetricKey _generateSymmetricKeyOfSize: keySizeInBits
350 algorithm: algorithm inKeychain: self];
353 - (MYPrivateKey*) generateRSAKeyPairOfSize: (unsigned)keySize {
354 return [MYPrivateKey _generateRSAKeyPairOfSize: keySize inKeychain: self];
358 - (CSSM_CSP_HANDLE) CSPHandle {
359 CSSM_CSP_HANDLE cspHandle = 0;
360 Assert(check(SecKeychainGetCSPHandle(self.keychainRefOrDefault, &cspHandle), @"SecKeychainGetCSPHandle"));
370 @implementation MYKeyEnumerator
372 - (id) initWithKeychain: (MYKeychain*)keychain
373 itemClass: (SecItemClass)itemClass
374 attributes: (SecKeychainAttribute[])attributes
375 count: (unsigned)count {
378 _keychain = [keychain retain];
379 _itemClass = itemClass;
380 SecKeychainAttributeList list = {.count=count, .attr=attributes};
381 if (!check(SecKeychainSearchCreateFromAttributes(keychain.keychainRef,
385 @"SecKeychainSearchCreateFromAttributes")) {
396 if (_search) CFRelease(_search);
403 if (_search) CFRelease(_search);
411 MYPublicKey *key = nil;
413 SecKeychainItemRef found = NULL;
414 OSStatus err = SecKeychainSearchCopyNext(_search, &found);
416 if (err != errSecItemNotFound)
417 check(err,@"SecKeychainSearchCopyNext");
423 switch (_itemClass) {
424 case kSecPrivateKeyItemClass: {
425 key = [[MYPrivateKey alloc] initWithKeyRef: (SecKeyRef)found];
428 case kSecCertificateItemClass:
429 key = [[[MYCertificate alloc] initWithCertificateRef: (SecCertificateRef)found] autorelease];
431 case kSecPublicKeyItemClass:
432 key = [[[MYPublicKey alloc] initWithKeyRef: (SecKeyRef)found] autorelease];
444 @implementation MYIdentityEnumerator
446 - (id) initWithKeychain: (MYKeychain*)keychain {
449 if (!check(SecIdentitySearchCreate(keychain.keychainRef, 0, &_searchRef),
450 @"SecIdentitySearchCreate")) {
459 SecIdentityRef identityRef = NULL;
460 OSStatus err = SecIdentitySearchCopyNext(_searchRef, &identityRef);
461 if (err==errKCItemNotFound || !check(err, @"SecIdentitySearchCopyNext"))
463 return [[[MYIdentity alloc] initWithIdentityRef: identityRef] autorelease];
469 #endif !MYCRYPTO_USE_IPHONE_API
474 Copyright (c) 2009, Jens Alfke <jens@mooseyard.com>. All rights reserved.
476 Redistribution and use in source and binary forms, with or without modification, are permitted
477 provided that the following conditions are met:
479 * Redistributions of source code must retain the above copyright notice, this list of conditions
480 and the following disclaimer.
481 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
482 and the following disclaimer in the documentation and/or other materials provided with the
485 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
486 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
487 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
488 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
489 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
490 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
491 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
492 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.