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