Whew! MYParsedCertificate can now generate certs from scratch. Also added improvements and fixes to the BER/DER codecs.
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]);
209 + (void) setUserInteractionAllowed: (BOOL)allowed {
210 SecKeychainSetUserInteractionAllowed(allowed);
215 #pragma mark SEARCHING:
218 - (MYKeychainItem*) itemOfClass: (SecItemClass)itemClass
219 withDigest: (MYSHA1Digest*)pubKeyDigest
221 SecKeychainAttribute attr = {.tag= (itemClass==kSecCertificateItemClass ?kSecPublicKeyHashItemAttr :kSecKeyLabel),
222 .length= pubKeyDigest.length,
223 .data= (void*) pubKeyDigest.bytes};
224 MYKeyEnumerator *e = [[MYKeyEnumerator alloc] initWithKeychain: self
226 attributes: &attr count: 1];
227 MYKeychainItem *item = e.nextObject;
232 - (MYPublicKey*) publicKeyWithDigest: (MYSHA1Digest*)pubKeyDigest {
233 return (MYPublicKey*) [self itemOfClass: kSecPublicKeyItemClass withDigest: pubKeyDigest];
236 - (NSEnumerator*) enumeratePublicKeys {
237 return [[[MYKeyEnumerator alloc] initWithKeychain: self
238 itemClass: kSecPublicKeyItemClass
239 attributes: NULL count: 0] autorelease];
242 - (NSEnumerator*) publicKeysWithAlias: (NSString*)alias {
243 NSData *utf8 = [alias dataUsingEncoding: NSUTF8StringEncoding];
244 SecKeychainAttribute attr = {.tag=kSecKeyAlias, .length=utf8.length, .data=(void*)utf8.bytes};
245 return [[[MYKeyEnumerator alloc] initWithKeychain: self
246 itemClass: kSecPublicKeyItemClass
247 attributes: &attr count: 1] autorelease];
251 - (MYPrivateKey*) privateKeyWithDigest: (MYSHA1Digest*)pubKeyDigest {
252 return (MYPrivateKey*) [self itemOfClass: kSecPrivateKeyItemClass withDigest: pubKeyDigest];
255 - (NSEnumerator*) enumeratePrivateKeys {
256 return [[[MYKeyEnumerator alloc] initWithKeychain: self
257 itemClass: kSecPrivateKeyItemClass
258 attributes: NULL count: 0] autorelease];
261 - (MYCertificate*) certificateWithDigest: (MYSHA1Digest*)pubKeyDigest {
262 return (MYCertificate*) [self itemOfClass: kSecCertificateItemClass withDigest: pubKeyDigest];
265 - (NSEnumerator*) enumerateCertificates {
266 return [[[MYKeyEnumerator alloc] initWithKeychain: self
267 itemClass: kSecCertificateItemClass
268 attributes: NULL count: 0] autorelease];
271 - (NSEnumerator*) enumerateIdentities {
272 return [[[MYIdentityEnumerator alloc] initWithKeychain: self] autorelease];
275 - (NSEnumerator*) enumerateSymmetricKeys {
276 return [[[MYKeyEnumerator alloc] initWithKeychain: self
277 itemClass: kSecSymmetricKeyItemClass
278 attributes: NULL count: 0] autorelease];
281 - (NSEnumerator*) symmetricKeysWithAlias: (NSString*)alias {
282 NSData *utf8 = [alias dataUsingEncoding: NSUTF8StringEncoding];
283 SecKeychainAttribute attr = {.tag=kSecKeyAlias, .length=utf8.length, .data=(void*)utf8.bytes};
284 return [[[MYKeyEnumerator alloc] initWithKeychain: self
285 itemClass: kSecSymmetricKeyItemClass
286 attributes: &attr count: 1] autorelease];
295 - (MYPublicKey*) importPublicKey: (NSData*)keyData {
296 return [[[MYPublicKey alloc] _initWithKeyData: keyData
297 forKeychain: self.keychainRefOrDefault]
301 - (MYPrivateKey*) importPublicKey: (NSData*)pubKeyData
302 privateKey: (NSData*)privKeyData
303 alertTitle: (NSString*)title
304 alertPrompt: (NSString*)prompt {
305 return [[[MYPrivateKey alloc] _initWithKeyData: privKeyData
306 publicKeyData: pubKeyData
307 forKeychain: self.keychainRefOrDefault
308 alertTitle: (NSString*)title
309 alertPrompt: (NSString*)prompt]
313 - (MYPrivateKey*) importPublicKey: (NSData*)pubKeyData
314 privateKey: (NSData*)privKeyData
316 return [self importPublicKey: pubKeyData privateKey: privKeyData
317 alertTitle: @"Import Private Key"
318 alertPrompt: @"To import your saved private key, please re-enter the "
319 "passphrase you used when you exported it."];
322 - (MYCertificate*) importCertificate: (NSData*)data
323 type: (CSSM_CERT_TYPE) type
324 encoding: (CSSM_CERT_ENCODING) encoding;
326 MYCertificate *cert = [[[MYCertificate alloc] initWithCertificateData: data
331 if (!check(SecCertificateAddToKeychain(cert.certificateRef, self.keychainRefOrDefault),
332 @"SecCertificateAddToKeychain"))
338 - (MYCertificate*) importCertificate: (NSData*)data {
339 return [self importCertificate: data
340 type: CSSM_CERT_X_509v3
341 encoding: CSSM_CERT_ENCODING_BER];
344 - (BOOL) addCertificate: (MYCertificate*)certificate {
346 return check(SecCertificateAddToKeychain(certificate.certificateRef, self.keychainRefOrDefault),
347 @"SecCertificateAddToKeychain");
351 - (MYSymmetricKey*) generateSymmetricKeyOfSize: (unsigned)keySizeInBits
352 algorithm: (CCAlgorithm)algorithm
354 return [MYSymmetricKey _generateSymmetricKeyOfSize: keySizeInBits
355 algorithm: algorithm inKeychain: self];
358 - (MYPrivateKey*) generateRSAKeyPairOfSize: (unsigned)keySize {
359 return [MYPrivateKey _generateRSAKeyPairOfSize: keySize inKeychain: self];
363 - (CSSM_CSP_HANDLE) CSPHandle {
364 CSSM_CSP_HANDLE cspHandle = 0;
365 Assert(check(SecKeychainGetCSPHandle(self.keychainRefOrDefault, &cspHandle), @"SecKeychainGetCSPHandle"));
375 @implementation MYKeyEnumerator
377 - (id) initWithKeychain: (MYKeychain*)keychain
378 itemClass: (SecItemClass)itemClass
379 attributes: (SecKeychainAttribute[])attributes
380 count: (unsigned)count {
383 _keychain = [keychain retain];
384 _itemClass = itemClass;
385 SecKeychainAttributeList list = {.count=count, .attr=attributes};
386 if (!check(SecKeychainSearchCreateFromAttributes(keychain.keychainRef,
390 @"SecKeychainSearchCreateFromAttributes")) {
401 if (_search) CFRelease(_search);
408 if (_search) CFRelease(_search);
416 MYPublicKey *key = nil;
418 SecKeychainItemRef found = NULL;
419 OSStatus err = SecKeychainSearchCopyNext(_search, &found);
421 if (err != errSecItemNotFound)
422 check(err,@"SecKeychainSearchCopyNext");
428 switch (_itemClass) {
429 case kSecPrivateKeyItemClass: {
430 key = [[MYPrivateKey alloc] initWithKeyRef: (SecKeyRef)found];
433 case kSecCertificateItemClass:
434 key = [[[MYCertificate alloc] initWithCertificateRef: (SecCertificateRef)found] autorelease];
436 case kSecPublicKeyItemClass:
437 key = [[[MYPublicKey alloc] initWithKeyRef: (SecKeyRef)found] autorelease];
449 @implementation MYIdentityEnumerator
451 - (id) initWithKeychain: (MYKeychain*)keychain {
454 if (!check(SecIdentitySearchCreate(keychain.keychainRef, 0, &_searchRef),
455 @"SecIdentitySearchCreate")) {
464 SecIdentityRef identityRef = NULL;
465 OSStatus err = SecIdentitySearchCopyNext(_searchRef, &identityRef);
466 if (err==errKCItemNotFound || !check(err, @"SecIdentitySearchCopyNext"))
468 return [[[MYIdentity alloc] initWithIdentityRef: identityRef] autorelease];
474 #endif !MYCRYPTO_USE_IPHONE_API
479 Copyright (c) 2009, Jens Alfke <jens@mooseyard.com>. All rights reserved.
481 Redistribution and use in source and binary forms, with or without modification, are permitted
482 provided that the following conditions are met:
484 * Redistributions of source code must retain the above copyright notice, this list of conditions
485 and the following disclaimer.
486 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
487 and the following disclaimer in the documentation and/or other materials provided with the
490 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
491 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
492 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
493 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
494 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
495 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
496 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
497 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.