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/2/09.
6 // Copyright 2009 Jens Alfke. All rights reserved.
9 #import "MYSymmetricKey.h"
11 #import "MYCrypto_Private.h"
13 #if !MYCRYPTO_USE_IPHONE_API
15 #import <Security/cssmtype.h>
18 CSSM_ALGORITHMS CSSMFromCCAlgorithm( CCAlgorithm ccAlgorithm ) {
19 static const CSSM_ALGORITHMS kCSSMAlgorithms[] = {
20 CSSM_ALGID_AES, CSSM_ALGID_DES, CSSM_ALGID_3DES_3KEY, CSSM_ALGID_CAST, CSSM_ALGID_RC4
22 if (ccAlgorithm >=0 && ccAlgorithm <= kCCAlgorithmRC4)
23 return kCSSMAlgorithms[ccAlgorithm];
25 return CSSM_ALGID_NONE;
28 static const char *kCCAlgorithmNames[] = {"AES", "DES", "DES^3", "CAST", "RC4"};
31 /** Undocumented Security function. Unfortunately this is the only way I can find to create
32 a SecKeyRef from a CSSM_KEY. */
33 extern OSStatus SecKeyCreate(const CSSM_KEY *key, SecKeyRef* keyRef) WEAK_IMPORT_ATTRIBUTE;
35 static CSSM_KEY* cssmKeyFromData( NSData *keyData, CSSM_ALGORITHMS algorithm,
36 MYKeychain *keychain);
39 static CSSM_KEY* unwrapCssmKeyFromData(NSData *wrappedData,
40 CSSM_ALGORITHMS algorithm,
42 static CSSM_ENCRYPT_MODE defaultModeForAlgorithm(CSSM_ALGORITHMS algorithm);
43 static CSSM_PADDING defaultPaddingForAlgorithm(CSSM_ALGORITHMS algorithm);
46 static CSSM_DATA makeSalt( id salty, size_t length );
47 static CSSM_RETURN impExpCreatePassKey(
48 const SecKeyImportExportParameters *keyParams, // required
49 CSSM_CSP_HANDLE cspHand, // MUST be CSPDL
50 BOOL verifyPhrase, // use 2nd passphrase textfield for verification?
51 CSSM_KEY_PTR *passKey); // mallocd and RETURNED
55 @implementation MYSymmetricKey
58 - (id) _initWithCSSMKey: (CSSM_KEY*)cssmKey {
63 SecKeyRef keyRef = NULL;
64 if (SecKeyCreate == NULL) {
65 // If weak-linked SPI fn no longer exists
66 Warn(@"Unable to call SecKeyCreate SPI -- not available");
70 if (!check(SecKeyCreate(cssmKey,&keyRef), @"SecKeyCreate")) {
74 self = [self initWithKeyRef: keyRef];
76 _ownedCSSMKey = cssmKey; // (so I'll remember to free it)
82 - (id) _initWithKeyData: (NSData*)keyData
83 algorithm: (CCAlgorithm)algorithm
84 inKeychain: (MYKeychain*)keychain
86 Assert(algorithm <= kCCAlgorithmRC4);
88 CSSM_KEY *key = cssmKeyFromData(keyData, CSSMFromCCAlgorithm(algorithm), keychain);
89 return [self _initWithCSSMKey: key];
92 - (id) initWithKeyData: (NSData*)keyData
93 algorithm: (CCAlgorithm)algorithm
95 return [self _initWithKeyData: keyData algorithm: algorithm inKeychain: nil];
98 + (MYSymmetricKey*) _generateSymmetricKeyOfSize: (unsigned)keySizeInBits
99 algorithm: (CCAlgorithm)algorithm
100 inKeychain: (MYKeychain*)keychain
102 Assert(algorithm <= kCCAlgorithmRC4);
103 CSSM_KEYATTR_FLAGS flags = CSSM_KEYATTR_EXTRACTABLE;
105 flags |= CSSM_KEYATTR_PERMANENT; // | CSSM_KEYATTR_SENSITIVE; //FIX: Re-enable this bit
107 flags |= CSSM_KEYATTR_RETURN_REF;
108 keychain = [MYKeychain defaultKeychain]; // establish a context for the key
110 CSSM_KEYUSE usage = CSSM_KEYUSE_ANY;
111 SecKeyRef keyRef = NULL;
112 if (!check(SecKeyGenerate(keychain.keychainRefOrDefault,
113 CSSMFromCCAlgorithm(algorithm),
115 0, usage, flags, NULL, &keyRef),
116 @"SecKeyGenerate")) {
119 return [[[self alloc] initWithKeyRef: keyRef] autorelease];
122 + (MYSymmetricKey*) generateSymmetricKeyOfSize: (unsigned)keySizeInBits
123 algorithm: (CCAlgorithm)algorithm {
124 return [self _generateSymmetricKeyOfSize: keySizeInBits
129 + (NSString*) promptForPassphraseWithAlertTitle: (NSString*)alertTitle
130 alertPrompt: (NSString*)prompt
131 creating: (BOOL)creating
133 // Ask the user for a passphrase:
134 SecKeyImportExportParameters params = {
135 .version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION,
136 .flags = kSecKeySecurePassphrase,
137 .alertTitle = (CFStringRef)alertTitle,
138 .alertPrompt = (CFStringRef)prompt,
139 .keyUsage = CSSM_KEYUSE_ANY,
140 .keyAttributes = CSSM_KEYATTR_EXTRACTABLE
142 CSSM_CSP_HANDLE cspHandle = [[MYKeychain defaultKeychain] CSPHandle];
143 CSSM_KEY *passphraseKey = NULL;
144 if (impExpCreatePassKey(¶ms,
147 &passphraseKey) != CSSM_OK)
150 MYSymmetricKey *key = [[self alloc] _initWithCSSMKey: passphraseKey];
151 NSData *keyData = key.keyData;
153 NSString *passphrase = [[NSString alloc] initWithData: keyData
154 encoding: NSUTF8StringEncoding];
156 return [passphrase autorelease];
160 #define PKCS5_V2_SALT_LEN 8
161 #define PKCS5_V2_ITERATIONS 2048
162 #define PKCS5_V2_DES_IV_SIZE 8
164 + (MYSymmetricKey*) generateFromUserPassphraseWithAlertTitle: (NSString*)alertTitle
165 alertPrompt: (NSString*)prompt
166 creating: (BOOL)creating
169 MYSymmetricKey *generatedKey = nil;
171 // Ask the user for a passphrase:
172 SecKeyImportExportParameters params = {
173 .version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION,
174 .flags = kSecKeySecurePassphrase,
175 .alertTitle = (CFStringRef)alertTitle,
176 .alertPrompt = (CFStringRef)prompt,
177 .keyUsage = CSSM_KEYUSE_ANY,
178 .keyAttributes = CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_SENSITIVE
180 CSSM_CSP_HANDLE cspHandle = [[MYKeychain defaultKeychain] CSPHandle];
181 CSSM_KEY *passphraseKey = NULL;
182 if (impExpCreatePassKey(¶ms,
185 &passphraseKey) != CSSM_OK)
188 CSSM_DATA saltData = makeSalt(saltObj,PKCS5_V2_SALT_LEN);
189 CSSM_CRYPTO_DATA seed = {};
191 // Now use the secure passphrase to generate a symmetric key:
192 CSSM_CC_HANDLE ctx = 0;
193 CSSM_ACCESS_CREDENTIALS credentials = {};
194 if (checkcssm(CSSM_CSP_CreateDeriveKeyContext(cspHandle,
195 CSSM_ALGID_PKCS5_PBKDF2,
203 @"CSSM_CSP_CreateDeriveKeyContext")) {
204 CSSM_PKCS5_PBKDF2_PARAMS params = {.PseudoRandomFunction=CSSM_PKCS5_PBKDF2_PRF_HMAC_SHA1};
205 CSSM_DATA paramData = {.Data=(void*)¶ms, .Length=sizeof(params)};
206 CSSM_KEY *cssmKey = calloc(1,sizeof(CSSM_KEY));
207 if (checkcssm(CSSM_DeriveKey(ctx,
210 CSSM_KEYATTR_EXTRACTABLE, //| CSSM_KEYATTR_SENSITIVE,
214 @"CSSM_DeriveKey")) {
215 generatedKey = [[[self alloc] _initWithCSSMKey: cssmKey] autorelease];
218 CSSM_DeleteContext(ctx);
219 CSSM_FreeKey(cspHandle, &credentials, passphraseKey, YES);
227 CSSM_FreeKey(self.cssmCSPHandle, NULL, _ownedCSSMKey, YES);
232 #if !TARGET_OS_IPHONE
233 - (id) initWithWrappedKeyData: (NSData*)wrappedKeyData {
234 return [self _initWithCSSMKey: unwrapCssmKeyFromData(wrappedKeyData,
235 CSSM_ALGID_AES,128)];
239 - (NSData*) exportWrappedKeyWithPassphrasePrompt: (NSString*)prompt
241 // Prompt user for a passphrase to use for the wrapping key:
242 MYSymmetricKey *wrappingKey = [MYSymmetricKey
243 generateFromUserPassphraseWithAlertTitle: @"Export Key"
246 salt: [MYCryptor randomKeyOfLength: PKCS5_V2_SALT_LEN*8]];
249 Log(@"Wrapping using %@",wrappingKey);
251 // Create the context:
252 CSSM_ACCESS_CREDENTIALS credentials = {};
253 CSSM_CSP_HANDLE cspHandle = self.cssmCSPHandle;
254 CSSM_ALGORITHMS algorithm = wrappingKey.cssmAlgorithm;
255 uint8 iv[16] = {0}; // Right size for AES. Are zeros OK? //FIX: Support other algorithms
256 CSSM_DATA ivData = {.Data=(void*)&iv, .Length=sizeof(iv)};
258 if (!checkcssm(CSSM_CSP_CreateSymmetricContext(cspHandle,
259 algorithm, //CSSM_ALGID_3DES_3KEY_EDE
260 defaultModeForAlgorithm(algorithm),
264 defaultPaddingForAlgorithm(algorithm),
267 @"CSSM_CSP_CreateSymmetricContext"))
271 NSData *result = nil;
272 CSSM_WRAP_KEY wrappedKey = {};
273 if (checkcssm(CSSM_WrapKey(ctx, &credentials, self.cssmKey, NULL, &wrappedKey),
275 // ...and copy the wrapped key data to the result NSData:
276 result = [NSData dataWithBytes: wrappedKey.KeyData.Data length: wrappedKey.KeyData.Length];
277 CSSM_FreeKey(cspHandle, &credentials, &wrappedKey, NO);
279 // Finally, delete the context
280 CSSM_DeleteContext(ctx);
286 - (SecExternalItemType) keyType {
287 return kSecItemTypeSessionKey;
290 - (CCAlgorithm) algorithm {
291 CSSM_ALGORITHMS cssmAlg;
292 cssmAlg = self.cssmKey->KeyHeader.AlgorithmId;
295 return kCCAlgorithmAES128;
297 return kCCAlgorithmDES;
298 case CSSM_ALGID_3DES_3KEY:
299 return kCCAlgorithm3DES;
300 case CSSM_ALGID_CAST:
301 return kCCAlgorithmCAST;
303 return kCCAlgorithmRC4;
305 Warn(@"CSSM_ALGORITHMS #%u doesn't map to any CCAlgorithm", cssmAlg);
306 return (CCAlgorithm)-1;
310 - (const char*) algorithmName {
311 CCAlgorithm a = self.algorithm;
312 if (a >= 0 && a <= kCCAlgorithmRC4)
313 return kCCAlgorithmNames[a];
318 - (unsigned) keySizeInBits {
319 const CSSM_KEY *key = self.cssmKey;
321 return key->KeyHeader.LogicalKeySizeInBits;
325 - (NSString*) description {
326 return $sprintf(@"%@[%u-bit %s]", [self class], self.keySizeInBits, self.algorithmName);
330 - (NSData*) _cryptData: (NSData*)data operation: (CCOperation)op options: (CCOptions)options
332 NSData *keyData = self.keyData;
333 Assert(keyData, @"Couldn't get key data");
334 NSMutableData *output = [NSMutableData dataWithLength: data.length + 256];
335 size_t bytesWritten = 0;
336 CCCryptorStatus status = CCCrypt(op, self.algorithm, options,
337 keyData.bytes, keyData.length, NULL,
338 data.bytes, data.length, output.mutableBytes, output.length,
341 Warn(@"MYSymmetricKey: CCCrypt returned error %i",status);
344 output.length = bytesWritten;
348 - (NSData*) encryptData: (NSData*)data {
349 return [self _cryptData: data operation: kCCEncrypt options: kCCOptionPKCS7Padding];
353 - (NSData*) decryptData: (NSData*)data {
354 return [self _cryptData: data operation: kCCDecrypt options: kCCOptionPKCS7Padding];
364 static CSSM_KEY* cssmKeyFromData( NSData *keyData,
365 CSSM_ALGORITHMS algorithm,
366 MYKeychain *keychain ) {
367 // Thanks to Jim Murphy for showing the way!
368 if (!keychain) keychain = [MYKeychain defaultKeychain];
369 CSSM_CC_HANDLE ccHandle;
370 if (!checkcssm(CSSM_CSP_CreateSymmetricContext([keychain CSPHandle],
371 CSSM_ALGID_NONE, CSSM_ALGMODE_WRAP,
373 CSSM_PADDING_NONE, NULL,
375 @"CSSM_CSP_CreateSymmetricContext"))
378 CSSM_KEY wrappedKey = {
380 .BlobType = CSSM_KEYBLOB_RAW,
381 .Format = CSSM_KEYBLOB_RAW_FORMAT_NONE,
382 .AlgorithmId = algorithm,
383 .KeyClass = CSSM_KEYCLASS_SESSION_KEY,
384 .LogicalKeySizeInBits = keyData.length*8,
385 .KeyAttr = CSSM_KEYATTR_EXTRACTABLE,
386 .KeyUsage = CSSM_KEYUSE_ANY
389 .Data = (void*)keyData.bytes,
390 .Length = keyData.length
394 CSSM_KEY *outKey = calloc(sizeof(CSSM_KEY),1);
396 if (!checkcssm(CSSM_UnwrapKey(ccHandle,
400 CSSM_KEYATTR_EXTRACTABLE,
405 @"CSSM_UnwrapKey")) {
409 CSSM_DeleteContext(ccHandle);
414 #if !TARGET_OS_IPHONE
415 static CSSM_KEY* unwrapCssmKeyFromData(NSData *wrappedData,
416 CSSM_ALGORITHMS algorithm,
417 unsigned sizeInBits) {
418 Warn(@"MYSymmetricKey: unwrapping is unimplemented; sorry");
420 #if 0 //not finished yet
421 // First create a wrapped-key structure from the data:
422 CSSM_WRAP_KEY wrappedKey = {
424 .BlobType = CSSM_KEYBLOB_WRAPPED,
425 .Format = CSSM_KEYBLOB_RAW_FORMAT_PKCS3,
426 .AlgorithmId = algorithm,
427 .KeyClass = CSSM_KEYCLASS_SESSION_KEY,
428 .LogicalKeySizeInBits = sizeInBits,
429 .KeyAttr = CSSM_KEYATTR_EXTRACTABLE,
430 .KeyUsage = CSSM_KEYUSE_ANY,
431 .WrapAlgorithmId = CSSM_ALGID_AES,
434 .Data = (void*)wrappedData.bytes,
435 .Length = wrappedData.length
443 // Create salt data of a specific length from an arbitrary NSObject. */
444 static CSSM_DATA makeSalt( id salty, size_t length ) {
445 // Convert to NSData if necessary:
447 if (![salty isKindOfClass: [NSData class]])
448 salty = [[salty description] dataUsingEncoding: NSUTF8StringEncoding];
449 // Repeat enough times to fill the desired length:
450 NSMutableData *salt = [[salty mutableCopy] autorelease];
451 CAssert(salt.length>0);
452 while (salt.length < length) {
453 [salt appendData: salt];
455 // Truncate to length and return it:
456 salt.length = length;
457 return (CSSM_DATA){.Data=(void*)salt.bytes, .Length=salt.length};
462 // Code from Keychain.framework:
464 #if !TARGET_OS_IPHONE
466 static CSSM_ENCRYPT_MODE defaultModeForAlgorithm(CSSM_ALGORITHMS algorithm) {
468 // 8-byte block ciphers
470 case CSSM_ALGID_3DES_3KEY_EDE:
473 return CSSM_ALGMODE_CBCPadIV8; break;
474 // 16-byte block ciphers
476 return CSSM_ALGMODE_CBCPadIV8; break;
480 return CSSM_ALGMODE_NONE; break;
483 Warn(@"Asked for the default mode for algorithm %d, but don't know that algorithm.\n", algorithm);
484 return CSSM_ALGMODE_NONE;
490 static CSSM_PADDING defaultPaddingForAlgorithm(CSSM_ALGORITHMS algorithm) {
492 /* 8-byte block ciphers */
494 case CSSM_ALGID_3DES_3KEY_EDE:
497 return CSSM_PADDING_PKCS5; break;
498 /* 16-byte block ciphers */
500 return CSSM_PADDING_PKCS7; break;
504 return CSSM_PADDING_NONE; break;
505 /* RSA/DSA asymmetric */
508 return CSSM_PADDING_PKCS1; break;
511 Warn(@"Asked for the default padding mode for %d, but don't know that algorithm.\n", algorithm);
512 return CSSM_PADDING_NONE;
519 // Code below was copied from SecImportExportUtils.cpp in Apple's libsecurity_keychain project
523 * Given a context specified via a CSSM_CC_HANDLE, add a new
524 * CSSM_CONTEXT_ATTRIBUTE to the context as specified by AttributeType,
525 * AttributeLength, and an untyped pointer.
527 static CSSM_RETURN impExpAddContextAttribute(CSSM_CC_HANDLE CCHandle,
528 uint32 AttributeType,
529 uint32 AttributeLength,
530 const void *AttributePtr)
532 CSSM_CONTEXT_ATTRIBUTE newAttr;
534 newAttr.AttributeType = AttributeType;
535 newAttr.AttributeLength = AttributeLength;
536 newAttr.Attribute.Data = (CSSM_DATA_PTR)AttributePtr;
537 return CSSM_UpdateContextAttributes(CCHandle, 1, &newAttr);
541 * Add a CFString to a crypto context handle.
543 static CSSM_RETURN impExpAddStringAttr(
544 CSSM_CC_HANDLE ccHand,
546 CSSM_ATTRIBUTE_TYPE attrType)
548 /* CFStrings are passed as external rep in UTF8 encoding by convention */
550 outData = CFStringCreateExternalRepresentation(NULL,
551 str, kCFStringEncodingUTF8, 0); // lossByte 0 ==> no loss allowed
552 if(outData == NULL) {
553 Warn(@"impExpAddStringAttr: bad string format");
558 attrData.Data = (uint8 *)CFDataGetBytePtr(outData);
559 attrData.Length = CFDataGetLength(outData);
560 CSSM_RETURN crtn = impExpAddContextAttribute(ccHand, attrType, sizeof(CSSM_DATA),
564 Warn(@"impExpAddStringAttr: CSSM_UpdateContextAttributes error");
570 * Generate a secure passphrase key. Caller must eventually CSSM_FreeKey the result.
572 static CSSM_RETURN impExpCreatePassKey(
573 const SecKeyImportExportParameters *keyParams, // required
574 CSSM_CSP_HANDLE cspHand, // MUST be CSPDL
575 BOOL verifyPhrase, // use 2nd passphrase textfield for verification?
576 CSSM_KEY_PTR *passKey) // mallocd and RETURNED
579 CSSM_CC_HANDLE ccHand;
581 CSSM_DATA dummyLabel;
582 CSSM_KEY_PTR ourKey = NULL;
584 Log(@"Generating secure passphrase key");
585 ourKey = (CSSM_KEY_PTR)malloc(sizeof(CSSM_KEY));
589 memset(ourKey, 0, sizeof(CSSM_KEY));
591 crtn = CSSM_CSP_CreateKeyGenContext(cspHand,
592 CSSM_ALGID_SECURE_PASSPHRASE,
593 4, // keySizeInBits must be non zero
601 checkcssm(crtn,@"CSSM_CSP_CreateKeyGenContext");
604 /* subsequent errors to errOut: */
606 /* additional context attributes specific to this type of key gen */
607 CAssert(keyParams != NULL); // or we wouldn't be here
608 if(keyParams->alertTitle != NULL) {
609 crtn = impExpAddStringAttr(ccHand, keyParams->alertTitle,
610 CSSM_ATTRIBUTE_ALERT_TITLE);
615 if(keyParams->alertPrompt != NULL) {
616 crtn = impExpAddStringAttr(ccHand, keyParams->alertPrompt,
617 CSSM_ATTRIBUTE_PROMPT);
622 verifyAttr = verifyPhrase ? 1 : 0;
623 crtn = impExpAddContextAttribute(ccHand, CSSM_ATTRIBUTE_VERIFY_PASSPHRASE,
624 sizeof(uint32), (const void *)verifyAttr);
626 checkcssm(crtn,@"impExpAddContextAttribute");
630 dummyLabel.Data = (uint8 *)"Secure Passphrase";
631 dummyLabel.Length = strlen((char *)dummyLabel.Data);
633 uint32 keyAttr = keyParams->keyAttributes;
634 if (keyAttr & CSSM_KEYATTR_SENSITIVE)
635 keyAttr |= CSSM_KEYATTR_RETURN_REF;
637 keyAttr |= CSSM_KEYATTR_EXTRACTABLE;
639 crtn = CSSM_GenerateKey(ccHand,
640 keyParams->keyUsage ?: CSSM_KEYUSE_ANY,
646 checkcssm(crtn,@"CSSM_GenerateKey");
649 CSSM_DeleteContext(ccHand);
650 if(crtn == CSSM_OK) {
653 else if(ourKey != NULL) {
660 #endif !MYCRYPTO_USE_IPHONE_API
665 Copyright (c) 2009, Jens Alfke <jens@mooseyard.com>. All rights reserved.
667 Redistribution and use in source and binary forms, with or without modification, are permitted
668 provided that the following conditions are met:
670 * Redistributions of source code must retain the above copyright notice, this list of conditions
671 and the following disclaimer.
672 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
673 and the following disclaimer in the documentation and/or other materials provided with the
676 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
677 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
678 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
679 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
680 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
681 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
682 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
683 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.