MYKey.m
author Jens Alfke <jens@mooseyard.com>
Wed Jun 10 09:02:18 2009 -0700 (2009-06-10)
changeset 25 38c3c3923e1f
parent 21 2c300b15b381
child 26 d9c2a06d4e4e
permissions -rw-r--r--
Changed the X.509 version number in generated certs from 1 to 3, so that SecCertificateCreateFromData on iPhone will accept them. :-/
     1 //
     2 //  MYKey.m
     3 //  MYCrypto
     4 //
     5 //  Created by Jens Alfke on 3/21/09.
     6 //  Copyright 2009 Jens Alfke. All rights reserved.
     7 //
     8 
     9 #import "MYKey.h"
    10 #import "MYCrypto_Private.h"
    11 #import "MYDigest.h"
    12 #import "MYErrorUtils.h"
    13 
    14 #if !MYCRYPTO_USE_IPHONE_API
    15 
    16 
    17 #pragma mark -
    18 @implementation MYKey
    19 
    20 
    21 - (id) initWithKeyRef: (SecKeyRef)key {
    22     return [super initWithKeychainItemRef: (SecKeychainItemRef)key];
    23 }
    24 
    25 - (id) _initWithKeyData: (NSData*)keyData
    26             forKeychain: (SecKeychainRef)keychain {
    27     Assert(keyData!=nil);
    28     SecKeyImportExportParameters params = {};
    29     SecKeyRef key = importKey(keyData, self.keyClass, keychain, &params);
    30     if (!key) {
    31         [self release];
    32         return nil;
    33     }
    34     self = [self initWithKeyRef: key];
    35     CFRelease(key);
    36     return self;
    37 }
    38 
    39 - (id) initWithKeyData: (NSData*)data {
    40     return [self _initWithKeyData: data forKeychain: nil];
    41 }
    42 
    43 
    44 - (NSString*) description {
    45     return $sprintf(@"%@[%@ /%p]", [self class], (self.name ?:@""), self.keychainItemRef);
    46 }
    47 
    48 - (SecExternalItemType) keyClass {
    49     AssertAbstractMethod();
    50 }
    51 
    52 #if MYCRYPTO_USE_IPHONE_API
    53 - (SecExternalItemType) keyType {
    54     return NULL;
    55 }
    56 #endif
    57 
    58 - (SecKeyRef) keyRef {
    59     return (SecKeyRef) self.keychainItemRef;
    60 }
    61 
    62 - (const CSSM_KEY*) cssmKey {
    63     const CSSM_KEY *cssmKey = NULL;
    64     Assert(check(SecKeyGetCSSMKey(self.keyRef, &cssmKey), @"SecKeyGetCSSMKey"), 
    65            @"Failed to get CSSM_KEY");
    66     return cssmKey;
    67 }
    68 
    69 - (const CSSM_CSP_HANDLE) cssmCSPHandle {
    70     CSSM_CSP_HANDLE cspHandle = 0;
    71     Assert(check(SecKeyGetCSPHandle(self.keyRef, &cspHandle), @"SecKeyGetCSPHandle"),
    72            @"Failed to get CSSM_CSP_HANDLE");
    73     return cspHandle;
    74 }
    75 
    76 - (CSSM_ALGORITHMS) cssmAlgorithm {
    77     return self.cssmKey->KeyHeader.AlgorithmId;
    78 }
    79 
    80 - (const CSSM_ACCESS_CREDENTIALS*) cssmCredentialsForOperation: (CSSM_ACL_AUTHORIZATION_TAG)operation
    81                                                           type: (SecCredentialType)type
    82                                                          error: (NSError**)outError
    83 {
    84     const CSSM_ACCESS_CREDENTIALS *credentials = NULL;
    85     OSStatus err = SecKeyGetCredentials(self.keyRef,
    86                                         operation,
    87                                         type,
    88                                         &credentials);
    89     if (!MYReturnError(outError, err,NSOSStatusErrorDomain, @"Couldn't get credentials for key"))
    90         return NULL;
    91     return credentials;
    92 }
    93 
    94 - (SecExternalFormat) _externalFormat {
    95     return kSecFormatRawKey;
    96 }
    97 
    98 - (NSData*) keyData {
    99     CFDataRef data = NULL;
   100     if (check(SecKeychainItemExport(self.keyRef, self._externalFormat, 0, NULL, &data),
   101               @"SecKeychainItemExport"))
   102         return [(id)CFMakeCollectable(data) autorelease];
   103     else
   104         return nil;
   105 }
   106 
   107 - (NSString*) name {
   108     return [self stringValueOfAttribute: kSecKeyPrintName];
   109 }
   110 
   111 - (void) setName: (NSString*)name {
   112     [self setValue: name ofAttribute: kSecKeyPrintName];
   113 }
   114 
   115 - (NSString*) comment {
   116     return [self stringValueOfAttribute: kSecKeyApplicationTag];
   117 }
   118 
   119 - (void) setComment: (NSString*)comment {
   120     [self setValue: comment ofAttribute: kSecKeyApplicationTag];
   121 }
   122 
   123 - (NSString*) alias {
   124     return [self stringValueOfAttribute: kSecKeyAlias];
   125 }
   126 
   127 - (void) setAlias: (NSString*)alias {
   128     [self setValue: alias ofAttribute: kSecKeyAlias];
   129 }
   130 
   131 
   132 #pragma mark -
   133 #pragma mark UTILITY FUNCTIONS:
   134 
   135 
   136 SecKeyRef importKey(NSData *data, 
   137                     SecExternalItemType type,
   138                     SecKeychainRef keychain,
   139                     SecKeyImportExportParameters *params) {
   140     SecExternalFormat inputFormat = (type==kSecItemTypeSessionKey) ?kSecFormatRawKey :kSecFormatUnknown;
   141     CFArrayRef items = NULL;
   142     
   143     params->version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
   144     params->flags |= kSecKeyImportOnlyOne;
   145     params->keyAttributes |= CSSM_KEYATTR_EXTRACTABLE;
   146     if (keychain) {
   147         params->keyAttributes |= CSSM_KEYATTR_PERMANENT;
   148         if (type==kSecItemTypeSessionKey)
   149             params->keyUsage = CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_DECRYPT;
   150         else if (type==kSecItemTypePublicKey)
   151             params->keyUsage = CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_VERIFY | CSSM_KEYUSE_WRAP;
   152         else if (type==kSecItemTypePrivateKey)
   153             params->keyUsage = CSSM_KEYUSE_DECRYPT | CSSM_KEYUSE_SIGN;
   154     }
   155     if (!check(SecKeychainItemImport((CFDataRef)data, NULL, &inputFormat, &type,
   156                                      0, params, keychain, &items),
   157                @"SecKeychainItemImport"))
   158         return nil;
   159     if (!items || CFArrayGetCount(items) != 1)
   160         return nil;
   161     SecKeyRef key = (SecKeyRef)CFRetain(CFArrayGetValueAtIndex(items,0));
   162     CFRelease(items);
   163     return key; // caller must CFRelease
   164 }
   165 
   166 
   167 - (MYSHA1Digest*) _keyDigest {
   168     MYSHA1Digest *digest = nil;
   169     CSSM_DATA *keyDigest = NULL;
   170     CSSM_CC_HANDLE context = [self _createPassThroughContext];
   171     if (context) {
   172         if (checkcssm(CSSM_CSP_PassThrough(context, CSSM_APPLECSP_KEYDIGEST, NULL, (void**)&keyDigest),
   173                       @"CSSM_CSP_PassThrough")) {
   174             if (keyDigest && keyDigest->Data) {
   175                 digest = [[[MYSHA1Digest alloc] initWithRawDigest: keyDigest->Data
   176                                                            length: keyDigest->Length] autorelease];
   177             }
   178         } else {
   179             SecKeyRef keyRef = self.keyRef;
   180             // Note - CSSM_CSP_PassThrough fails on a couple of private keys I've seen; it seems to
   181             // be ones that are either expired or don't have a matching public key at all (?)
   182             Warn(@"Failed to get digest of SecKeyRef %p (name='%@' appTag='%@')", 
   183                  keyRef,
   184                  self.name,
   185                  self.comment);
   186             NSData *digestData = [[self class] _getAttribute: kSecKeyLabel 
   187                                                       ofItem: (SecKeychainItemRef)keyRef];
   188             if (digestData) {
   189                 digest = (MYSHA1Digest*) [MYSHA1Digest digestFromDigestData: digestData];
   190                 if (!digest)
   191                     Warn(@"Digest property of key %p was invalid SHA-1: %@", keyRef,digestData);
   192             }
   193         }
   194         CSSM_DeleteContext(context);
   195     }
   196     return digest;
   197 }
   198 
   199 
   200 /** Asymmetric encryption/decryption; used by MYPublicKey and MYPrivateKey. */
   201 - (NSData*) _crypt: (NSData*)data operation: (BOOL)operation {
   202     CAssert(data);
   203     const CSSM_ACCESS_CREDENTIALS *credentials;
   204     credentials = [self cssmCredentialsForOperation: (operation ?CSSM_ACL_AUTHORIZATION_ENCRYPT 
   205                                                                 :CSSM_ACL_AUTHORIZATION_DECRYPT) 
   206                                                type: kSecCredentialTypeDefault
   207                                               error: nil];
   208     if (!credentials)
   209         return nil;
   210     
   211     CSSM_CC_HANDLE ccHandle;
   212     if (!checkcssm(CSSM_CSP_CreateAsymmetricContext(self.cssmCSPHandle,
   213                                                     CSSM_ALGID_RSA,
   214                                                     credentials,
   215                                                     self.cssmKey,
   216                                                     CSSM_PADDING_PKCS1,
   217                                                     &ccHandle),
   218                    @"CSSM_CSP_CreateAsymmetricContext"))
   219         return nil;
   220     
   221     CSSM_DATA original = {data.length, (void*)data.bytes};
   222     CSSM_DATA result = {};
   223     size_t outputLength;
   224     BOOL ok;
   225     if (operation)
   226         ok = checkcssm(CSSM_EncryptData(ccHandle, &original, 1, &result, 1, &outputLength, &result),
   227                        @"CSSM_EncryptData");
   228     else
   229         ok = checkcssm(CSSM_DecryptData(ccHandle, &original, 1, &result, 1, &outputLength, &result),
   230                        @"CSSM_DecryptData");
   231     CSSM_DeleteContext(ccHandle);
   232     return ok ?[NSData dataWithBytesNoCopy: result.Data length: outputLength freeWhenDone: YES] :nil;
   233 }
   234 
   235 
   236 - (CSSM_CC_HANDLE) _createSignatureContext: (CSSM_ALGORITHMS)algorithm {
   237     const CSSM_ACCESS_CREDENTIALS *credentials;
   238     credentials = [self cssmCredentialsForOperation: CSSM_ACL_AUTHORIZATION_SIGN 
   239                                                type: kSecCredentialTypeDefault
   240                                               error: nil];
   241     if (credentials) {
   242         CSSM_CC_HANDLE ccHandle = 0;
   243         if (checkcssm(CSSM_CSP_CreateSignatureContext(self.cssmCSPHandle, 
   244                                                       algorithm, 
   245                                                       credentials,
   246                                                       self.cssmKey,
   247                                                       &ccHandle),
   248                              @"CSSM_CSP_CreateSignatureContext") )
   249             return ccHandle;
   250     }
   251     return 0;
   252 }
   253 
   254 - (CSSM_CC_HANDLE) _createPassThroughContext
   255 {
   256     CSSM_CC_HANDLE ccHandle = 0;
   257     if (checkcssm(CSSM_CSP_CreatePassThroughContext(self.cssmCSPHandle, self.cssmKey, &ccHandle), 
   258                           @"CSSM_CSP_CreatePassThroughContext") )
   259         return ccHandle;
   260     else
   261         return 0;
   262 }
   263 
   264 
   265 @end
   266 
   267 #endif MYCRYPTO_USE_IPHONE_API
   268 
   269 
   270 
   271 /*
   272  Copyright (c) 2009, Jens Alfke <jens@mooseyard.com>. All rights reserved.
   273  
   274  Redistribution and use in source and binary forms, with or without modification, are permitted
   275  provided that the following conditions are met:
   276  
   277  * Redistributions of source code must retain the above copyright notice, this list of conditions
   278  and the following disclaimer.
   279  * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
   280  and the following disclaimer in the documentation and/or other materials provided with the
   281  distribution.
   282  
   283  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
   284  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
   285  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
   286  BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   287  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
   288   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
   289  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
   290  THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   291  */