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