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