Whew! MYParsedCertificate can now generate certs from scratch. Also added improvements and fixes to the BER/DER codecs.
5 // Created by Jens Alfke on 4/7/09.
6 // Copyright 2009 Jens Alfke. All rights reserved.
9 #import "MYPrivateKey.h"
10 #import "MYCrypto_Private.h"
12 #import <CommonCrypto/CommonDigest.h>
18 @implementation MYPrivateKey
21 - (id) initWithKeyRef: (SecKeyRef)privateKey
23 self = [super initWithKeyRef: privateKey];
25 // No public key given, so look it up:
26 MYSHA1Digest *digest = self._keyDigest;
28 _publicKey = [[self.keychain publicKeyWithDigest: digest] retain];
30 // The matching public key won't turn up if it's embedded in a certificate;
31 // I'd have to search for certs if I wanted to look that up. Skip it for now.
32 Log(@"MYPrivateKey(%p): Couldn't find matching public key for private key! digest=%@",
42 - (id) _initWithKeyRef: (SecKeyRef)privateKey
43 publicKey: (MYPublicKey*)publicKey
46 self = [super initWithKeyRef: privateKey];
48 _publicKey = [publicKey retain];
53 - (id) initWithKeyRef: (SecKeyRef)privateKey
54 publicKeyRef: (SecKeyRef)publicKeyRef
56 MYPublicKey *publicKey = [[MYPublicKey alloc] initWithKeyRef: publicKeyRef];
57 self = [self _initWithKeyRef: privateKey publicKey: publicKey];
62 - (id) _initWithKeyRef: (SecKeyRef)privateKey
63 publicKeyData: (NSData*)pubKeyData
64 forKeychain: (SecKeychainRef)keychain
70 MYPublicKey *pubKey = [[MYPublicKey alloc] _initWithKeyData: pubKeyData forKeychain: keychain];
75 self = [super initWithKeyRef: privateKey];
79 [pubKey removeFromKeychain];
88 // The public API for this is in MYKeychain.
89 - (id) _initWithKeyData: (NSData*)privKeyData
90 publicKeyData: (NSData*)pubKeyData
91 forKeychain: (SecKeychainRef)keychain
92 alertTitle: (NSString*)title
93 alertPrompt: (NSString*)prompt
95 // Try to import the private key first, since the user might cancel the passphrase alert.
96 SecKeyImportExportParameters params = {
97 .flags = kSecKeySecurePassphrase,
98 .alertTitle = (CFStringRef) title,
99 .alertPrompt = (CFStringRef) prompt
101 SecKeyRef privateKey = importKey(privKeyData,kSecItemTypePrivateKey,keychain,¶ms);
102 return [self _initWithKeyRef: privateKey publicKeyData: pubKeyData forKeychain: keychain];
105 // This method is for testing, so unit-tests don't require user intervention.
106 // It's deliberately not made public, to discourage clients from trying to manage the passphrases
107 // themselves (this is less secure than letting the Security agent do it.)
108 - (id) _initWithKeyData: (NSData*)privKeyData
109 publicKeyData: (NSData*)pubKeyData
110 forKeychain: (SecKeychainRef)keychain
111 passphrase: (NSString*)passphrase
113 SecKeyImportExportParameters params = {
114 .passphrase = (CFStringRef) passphrase,
116 SecKeyRef privateKey = importKey(privKeyData,kSecItemTypePrivateKey,keychain,¶ms);
117 return [self _initWithKeyRef: privateKey publicKeyData: pubKeyData forKeychain: keychain];
121 - (MYIdentity*) createSelfSignedIdentityWithAttributes: (NSDictionary*)attributes {
122 return MYIdentityCreateSelfSigned(self, attributes);
131 [_publicKey release];
136 + (MYPrivateKey*) _generateRSAKeyPairOfSize: (unsigned)keySize
137 inKeychain: (MYKeychain*)keychain
139 Assert( keySize == 512 || keySize == 1024 || keySize == 2048, @"Unsupported key size %u", keySize );
140 SecKeyRef pubKey=NULL, privKey=NULL;
143 #if MYCRYPTO_USE_IPHONE_API
144 NSDictionary *pubKeyAttrs = $dict({(id)kSecAttrIsPermanent, $true});
145 NSDictionary *privKeyAttrs = $dict({(id)kSecAttrIsPermanent, $true});
146 NSDictionary *keyAttrs = $dict( {(id)kSecAttrKeyType, (id)kSecAttrKeyTypeRSA},
147 {(id)kSecAttrKeySizeInBits, $object(keySize)},
148 {(id)kSecPublicKeyAttrs, pubKeyAttrs},
149 {(id)kSecPrivateKeyAttrs, privKeyAttrs} );
150 err = SecKeyGeneratePair((CFDictionaryRef)keyAttrs,&pubKey,&privKey);
152 err = SecKeyCreatePair(keychain.keychainRefOrDefault,
156 CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_VERIFY | CSSM_KEYUSE_WRAP, // public key
157 CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_PERMANENT,
158 CSSM_KEYUSE_ANY, // private key
159 CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_SENSITIVE,
160 NULL, // SecAccessRef
163 if (!check(err, @"SecKeyCreatePair")) {
166 return [[[self alloc] initWithKeyRef: privKey publicKeyRef: pubKey] autorelease];
171 #pragma mark ACCESSORS:
174 - (NSString*) description {
175 return $sprintf(@"%@[%@ %@ /%p]", [self class],
176 self.publicKeyDigest.abbreviatedHexString,
178 self.keychainItemRef);
181 @synthesize publicKey=_publicKey;
183 - (MYSHA1Digest*) publicKeyDigest {
184 return _publicKey.publicKeyDigest;
187 - (SecExternalItemType) keyType {
188 #if MYCRYPTO_USE_IPHONE_API
189 return kSecAttrKeyClassPublic;
191 return kSecItemTypePrivateKey;
195 - (NSData *) keyData {
196 [NSException raise: NSGenericException format: @"Can't access keyData of a PrivateKey"];
200 - (BOOL) setValue: (NSString*)valueStr ofAttribute: (SecKeychainAttrType)attr {
201 return [super setValue: valueStr ofAttribute: attr]
202 && [_publicKey setValue: valueStr ofAttribute: attr];
207 #pragma mark OPERATIONS:
210 - (BOOL) removeFromKeychain {
211 return [super removeFromKeychain]
212 && [_publicKey removeFromKeychain];
216 - (NSData*) rawDecryptData: (NSData*)data {
217 return [self _crypt: data operation: NO];
221 - (NSData*) signData: (NSData*)data {
223 #if MYCRYPTO_USE_IPHONE_API
224 uint8_t digest[CC_SHA1_DIGEST_LENGTH];
225 CC_SHA1(data.bytes,data.length, digest);
227 size_t sigLen = 1024;
228 uint8_t sigBuf[sigLen];
229 OSStatus err = SecKeyRawSign(self.keyRef, kSecPaddingPKCS1SHA1,
230 digest,sizeof(digest), //data.bytes, data.length,
233 Warn(@"SecKeyRawSign failed: %i",err);
236 return [NSData dataWithBytes: sigBuf length: sigLen];
238 NSData *signature = nil;
239 CSSM_CC_HANDLE ccHandle = [self _createSignatureContext: CSSM_ALGID_SHA1WithRSA];
240 if (!ccHandle) return nil;
241 CSSM_DATA original = {data.length, (void*)data.bytes};
242 CSSM_DATA result = {0,NULL};
243 if (checkcssm(CSSM_SignData(ccHandle, &original, 1, CSSM_ALGID_NONE, &result), @"CSSM_SignData"))
244 signature = [NSData dataWithBytesNoCopy: result.Data length: result.Length
246 CSSM_DeleteContext(ccHandle);
252 #if !TARGET_OS_IPHONE
254 - (NSData*) exportKeyInFormat: (SecExternalFormat)format
255 withPEM: (BOOL)withPEM
256 alertTitle: (NSString*)title
257 alertPrompt: (NSString*)prompt
259 SecKeyImportExportParameters params = {
260 .version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION,
261 .flags = kSecKeySecurePassphrase,
262 .alertTitle = (CFStringRef)title,
263 .alertPrompt = (CFStringRef)prompt
265 CFDataRef data = NULL;
266 if (check(SecKeychainItemExport(self.keyRef,
267 format, (withPEM ?kSecItemPemArmour :0),
269 @"SecKeychainItemExport"))
270 return [(id)CFMakeCollectable(data) autorelease];
275 - (NSData*) exportKey {
276 return [self exportKeyInFormat: kSecFormatWrappedOpenSSL withPEM: YES
277 alertTitle: @"Export Private Key"
278 alertPrompt: @"Enter a passphrase to protect the private-key file.\n"
279 "You will need to re-enter the passphrase later when importing the key from this file, "
280 "so keep it in a safe place."];
281 //FIX: Should make these messages localizable.
285 - (NSData*) _exportKeyInFormat: (SecExternalFormat)format
286 withPEM: (BOOL)withPEM
287 passphrase: (NSString*)passphrase
289 SecKeyImportExportParameters params = {
290 .version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION,
291 .passphrase = (CFStringRef)passphrase
293 CFDataRef data = NULL;
294 if (check(SecKeychainItemExport(self.keyRef,
295 format, (withPEM ?kSecItemPemArmour :0),
297 @"SecKeychainItemExport"))
298 return [(id)CFMakeCollectable(data) autorelease];
304 - (MYSymmetricKey*) unwrapSessionKey: (NSData*)wrappedData
305 withAlgorithm: (CCAlgorithm)algorithm
306 sizeInBits: (unsigned)sizeInBits
308 // First create a wrapped-key structure from the data:
309 CSSM_WRAP_KEY wrappedKey = {
311 .BlobType = CSSM_KEYBLOB_WRAPPED,
312 .Format = CSSM_KEYBLOB_RAW_FORMAT_PKCS3,
313 .AlgorithmId = CSSMFromCCAlgorithm(algorithm),
314 .KeyClass = CSSM_KEYCLASS_SESSION_KEY,
315 .LogicalKeySizeInBits = sizeInBits,
316 .KeyAttr = CSSM_KEYATTR_EXTRACTABLE,
317 .KeyUsage = CSSM_KEYUSE_ANY,
318 .WrapAlgorithmId = self.cssmAlgorithm,
321 .Data = (void*)wrappedData.bytes,
322 .Length = wrappedData.length
326 const CSSM_ACCESS_CREDENTIALS* credentials;
327 credentials = [self cssmCredentialsForOperation: CSSM_ACL_AUTHORIZATION_IMPORT_WRAPPED
328 type: kSecCredentialTypeDefault error: nil];
329 CSSM_CSP_HANDLE cspHandle = self.cssmCSPHandle;
331 if (!checkcssm(CSSM_CSP_CreateAsymmetricContext(cspHandle,
337 @"CSSM_CSP_CreateAsymmetricContext"))
340 // Now unwrap the key:
341 MYSymmetricKey *result = nil;
342 CSSM_KEY *unwrappedKey = calloc(1,sizeof(CSSM_KEY));
343 CSSM_DATA label = {.Data=(void*)"Imported key", .Length=strlen("Imported key")};
344 CSSM_DATA descriptiveData = {};
345 if (checkcssm(CSSM_UnwrapKey(ctx,
348 wrappedKey.KeyHeader.KeyUsage,
349 wrappedKey.KeyHeader.KeyAttr,
354 @"CSSM_UnwrapKey")) {
355 result = [[[MYSymmetricKey alloc] _initWithCSSMKey: unwrappedKey] autorelease];
357 // Finally, delete the context
360 CSSM_DeleteContext(ctx);
365 #endif !TARGET_OS_IPHONE
372 Copyright (c) 2009, Jens Alfke <jens@mooseyard.com>. All rights reserved.
374 Redistribution and use in source and binary forms, with or without modification, are permitted
375 provided that the following conditions are met:
377 * Redistributions of source code must retain the above copyright notice, this list of conditions
378 and the following disclaimer.
379 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
380 and the following disclaimer in the documentation and/or other materials provided with the
383 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
384 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
385 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
386 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
387 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
388 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
389 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
390 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.