Retired Document
Important: This document may not represent best practices for current development. Links to downloads and other resources may no longer be valid.
Classes/SecKeyWrapper.m
/* |
File: SecKeyWrapper.m |
Abstract: Core cryptographic wrapper class to exercise most of the Security |
APIs on the iPhone OS. Start here if all you are interested in are the |
cryptographic APIs on the iPhone OS. |
Version: 1.2 |
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc. |
("Apple") in consideration of your agreement to the following terms, and your |
use, installation, modification or redistribution of this Apple software |
constitutes acceptance of these terms. If you do not agree with these terms, |
please do not use, install, modify or redistribute this Apple software. |
In consideration of your agreement to abide by the following terms, and subject |
to these terms, Apple grants you a personal, non-exclusive license, under |
Apple's copyrights in this original Apple software (the "Apple Software"), to |
use, reproduce, modify and redistribute the Apple Software, with or without |
modifications, in source and/or binary forms; provided that if you redistribute |
the Apple Software in its entirety and without modifications, you must retain |
this notice and the following text and disclaimers in all such redistributions |
of the Apple Software. |
Neither the name, trademarks, service marks or logos of Apple Inc. may be used |
to endorse or promote products derived from the Apple Software without specific |
prior written permission from Apple. Except as expressly stated in this notice, |
no other rights or licenses, express or implied, are granted by Apple herein, |
including but not limited to any patent rights that may be infringed by your |
derivative works or by other works in which the Apple Software may be |
incorporated. |
The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO |
WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED |
WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN |
COMBINATION WITH YOUR PRODUCTS. |
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR |
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE |
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR |
DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF |
CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF |
APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
Copyright (C) 2008-2009 Apple Inc. All Rights Reserved. |
*/ |
#import "SecKeyWrapper.h" |
#import <Security/Security.h> |
@implementation SecKeyWrapper |
@synthesize publicTag, privateTag, symmetricTag, symmetricKeyRef; |
#if DEBUG |
#define LOGGING_FACILITY(X, Y) \ |
NSAssert(X, Y); |
#define LOGGING_FACILITY1(X, Y, Z) \ |
NSAssert1(X, Y, Z); |
#else |
#define LOGGING_FACILITY(X, Y) \ |
if (!(X)) { \ |
NSLog(Y); \ |
} |
#define LOGGING_FACILITY1(X, Y, Z) \ |
if (!(X)) { \ |
NSLog(Y, Z); \ |
} |
#endif |
#if TARGET_IPHONE_SIMULATOR |
#error This sample is designed to run on a device, not in the simulator. To run this sample, \ |
choose Project > Set Active SDK > Device and connect a device. Then click Build and Go. |
// Dummy implementations for no-building simulator target (reduce compiler warnings) |
+ (SecKeyWrapper *)sharedWrapper { return nil; } |
- (void)setObject:(id)inObject forKey:(id)key {} |
- (id)objectForKey:(id)key { return nil; } |
// Dummy implementations for my SecKeyWrapper class. |
- (void)generateKeyPair:(NSUInteger)keySize {} |
- (void)deleteAsymmetricKeys {} |
- (void)deleteSymmetricKey {} |
- (void)generateSymmetricKey {} |
- (NSData *)getSymmetricKeyBytes { return NULL; } |
- (SecKeyRef)addPeerPublicKey:(NSString *)peerName keyBits:(NSData *)publicKey { return NULL; } |
- (void)removePeerPublicKey:(NSString *)peerName {} |
- (NSData *)wrapSymmetricKey:(NSData *)symmetricKey keyRef:(SecKeyRef)publicKey { return nil; } |
- (NSData *)unwrapSymmetricKey:(NSData *)wrappedSymmetricKey { return nil; } |
- (NSData *)getSignatureBytes:(NSData *)plainText { return nil; } |
- (NSData *)getHashBytes:(NSData *)plainText { return nil; } |
- (BOOL)verifySignature:(NSData *)plainText secKeyRef:(SecKeyRef)publicKey signature:(NSData *)sig { return NO; } |
- (NSData *)doCipher:(NSData *)plainText key:(NSData *)symmetricKey context:(CCOperation)encryptOrDecrypt padding:(CCOptions *)pkcs7 { return nil; } |
- (SecKeyRef)getPublicKeyRef { return nil; } |
- (NSData *)getPublicKeyBits { return nil; } |
- (SecKeyRef)getPrivateKeyRef { return nil; } |
- (CFTypeRef)getPersistentKeyRefWithKeyRef:(SecKeyRef)keyRef { return NULL; } |
- (SecKeyRef)getKeyRefWithPersistentKeyRef:(CFTypeRef)persistentRef { return NULL; } |
#else |
// (See cssmtype.h and cssmapple.h on the Mac OS X SDK.) |
enum { |
CSSM_ALGID_NONE = 0x00000000L, |
CSSM_ALGID_VENDOR_DEFINED = CSSM_ALGID_NONE + 0x80000000L, |
CSSM_ALGID_AES |
}; |
// identifiers used to find public, private, and symmetric key. |
static const uint8_t publicKeyIdentifier[] = kPublicKeyTag; |
static const uint8_t privateKeyIdentifier[] = kPrivateKeyTag; |
static const uint8_t symmetricKeyIdentifier[] = kSymmetricKeyTag; |
static SecKeyWrapper * __sharedKeyWrapper = nil; |
/* Begin method definitions */ |
+ (SecKeyWrapper *)sharedWrapper { |
@synchronized(self) { |
if (__sharedKeyWrapper == nil) { |
[[self alloc] init]; |
} |
} |
return __sharedKeyWrapper; |
} |
+ (id)allocWithZone:(NSZone *)zone { |
@synchronized(self) { |
if (__sharedKeyWrapper == nil) { |
__sharedKeyWrapper = [super allocWithZone:zone]; |
return __sharedKeyWrapper; |
} |
} |
return nil; |
} |
- (id)copyWithZone:(NSZone *)zone { |
return self; |
} |
- (void)release { |
} |
- (id)retain { |
return self; |
} |
- (id)autorelease { |
return self; |
} |
- (NSUInteger)retainCount { |
return UINT_MAX; |
} |
-(id)init { |
if (self = [super init]) |
{ |
// Tag data to search for keys. |
privateTag = [[NSData alloc] initWithBytes:privateKeyIdentifier length:sizeof(privateKeyIdentifier)]; |
publicTag = [[NSData alloc] initWithBytes:publicKeyIdentifier length:sizeof(publicKeyIdentifier)]; |
symmetricTag = [[NSData alloc] initWithBytes:symmetricKeyIdentifier length:sizeof(symmetricKeyIdentifier)]; |
} |
return self; |
} |
- (void)deleteAsymmetricKeys { |
OSStatus sanityCheck = noErr; |
NSMutableDictionary * queryPublicKey = [[NSMutableDictionary alloc] init]; |
NSMutableDictionary * queryPrivateKey = [[NSMutableDictionary alloc] init]; |
// Set the public key query dictionary. |
[queryPublicKey setObject:(id)kSecClassKey forKey:(id)kSecClass]; |
[queryPublicKey setObject:publicTag forKey:(id)kSecAttrApplicationTag]; |
[queryPublicKey setObject:(id)kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType]; |
// Set the private key query dictionary. |
[queryPrivateKey setObject:(id)kSecClassKey forKey:(id)kSecClass]; |
[queryPrivateKey setObject:privateTag forKey:(id)kSecAttrApplicationTag]; |
[queryPrivateKey setObject:(id)kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType]; |
// Delete the private key. |
sanityCheck = SecItemDelete((CFDictionaryRef)queryPrivateKey); |
LOGGING_FACILITY1( sanityCheck == noErr || sanityCheck == errSecItemNotFound, @"Error removing private key, OSStatus == %d.", sanityCheck ); |
// Delete the public key. |
sanityCheck = SecItemDelete((CFDictionaryRef)queryPublicKey); |
LOGGING_FACILITY1( sanityCheck == noErr || sanityCheck == errSecItemNotFound, @"Error removing public key, OSStatus == %d.", sanityCheck ); |
[queryPrivateKey release]; |
[queryPublicKey release]; |
if (publicKeyRef) CFRelease(publicKeyRef); |
if (privateKeyRef) CFRelease(privateKeyRef); |
} |
- (void)deleteSymmetricKey { |
OSStatus sanityCheck = noErr; |
NSMutableDictionary * querySymmetricKey = [[NSMutableDictionary alloc] init]; |
// Set the symmetric key query dictionary. |
[querySymmetricKey setObject:(id)kSecClassKey forKey:(id)kSecClass]; |
[querySymmetricKey setObject:symmetricTag forKey:(id)kSecAttrApplicationTag]; |
[querySymmetricKey setObject:[NSNumber numberWithUnsignedInt:CSSM_ALGID_AES] forKey:(id)kSecAttrKeyType]; |
// Delete the symmetric key. |
sanityCheck = SecItemDelete((CFDictionaryRef)querySymmetricKey); |
LOGGING_FACILITY1( sanityCheck == noErr || sanityCheck == errSecItemNotFound, @"Error removing symmetric key, OSStatus == %d.", sanityCheck ); |
[querySymmetricKey release]; |
[symmetricKeyRef release]; |
} |
- (void)generateKeyPair:(NSUInteger)keySize { |
OSStatus sanityCheck = noErr; |
publicKeyRef = NULL; |
privateKeyRef = NULL; |
LOGGING_FACILITY1( keySize == 512 || keySize == 1024 || keySize == 2048, @"%d is an invalid and unsupported key size.", keySize ); |
// First delete current keys. |
[self deleteAsymmetricKeys]; |
// Container dictionaries. |
NSMutableDictionary * privateKeyAttr = [[NSMutableDictionary alloc] init]; |
NSMutableDictionary * publicKeyAttr = [[NSMutableDictionary alloc] init]; |
NSMutableDictionary * keyPairAttr = [[NSMutableDictionary alloc] init]; |
// Set top level dictionary for the keypair. |
[keyPairAttr setObject:(id)kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType]; |
[keyPairAttr setObject:[NSNumber numberWithUnsignedInteger:keySize] forKey:(id)kSecAttrKeySizeInBits]; |
// Set the private key dictionary. |
[privateKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecAttrIsPermanent]; |
[privateKeyAttr setObject:privateTag forKey:(id)kSecAttrApplicationTag]; |
// See SecKey.h to set other flag values. |
// Set the public key dictionary. |
[publicKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecAttrIsPermanent]; |
[publicKeyAttr setObject:publicTag forKey:(id)kSecAttrApplicationTag]; |
// See SecKey.h to set other flag values. |
// Set attributes to top level dictionary. |
[keyPairAttr setObject:privateKeyAttr forKey:(id)kSecPrivateKeyAttrs]; |
[keyPairAttr setObject:publicKeyAttr forKey:(id)kSecPublicKeyAttrs]; |
// SecKeyGeneratePair returns the SecKeyRefs just for educational purposes. |
sanityCheck = SecKeyGeneratePair((CFDictionaryRef)keyPairAttr, &publicKeyRef, &privateKeyRef); |
LOGGING_FACILITY( sanityCheck == noErr && publicKeyRef != NULL && privateKeyRef != NULL, @"Something really bad went wrong with generating the key pair." ); |
[privateKeyAttr release]; |
[publicKeyAttr release]; |
[keyPairAttr release]; |
} |
- (void)generateSymmetricKey { |
OSStatus sanityCheck = noErr; |
uint8_t * symmetricKey = NULL; |
// First delete current symmetric key. |
[self deleteSymmetricKey]; |
// Container dictionary |
NSMutableDictionary *symmetricKeyAttr = [[NSMutableDictionary alloc] init]; |
[symmetricKeyAttr setObject:(id)kSecClassKey forKey:(id)kSecClass]; |
[symmetricKeyAttr setObject:symmetricTag forKey:(id)kSecAttrApplicationTag]; |
[symmetricKeyAttr setObject:[NSNumber numberWithUnsignedInt:CSSM_ALGID_AES] forKey:(id)kSecAttrKeyType]; |
[symmetricKeyAttr setObject:[NSNumber numberWithUnsignedInt:(unsigned int)(kChosenCipherKeySize << 3)] forKey:(id)kSecAttrKeySizeInBits]; |
[symmetricKeyAttr setObject:[NSNumber numberWithUnsignedInt:(unsigned int)(kChosenCipherKeySize << 3)] forKey:(id)kSecAttrEffectiveKeySize]; |
[symmetricKeyAttr setObject:(id)kCFBooleanTrue forKey:(id)kSecAttrCanEncrypt]; |
[symmetricKeyAttr setObject:(id)kCFBooleanTrue forKey:(id)kSecAttrCanDecrypt]; |
[symmetricKeyAttr setObject:(id)kCFBooleanFalse forKey:(id)kSecAttrCanDerive]; |
[symmetricKeyAttr setObject:(id)kCFBooleanFalse forKey:(id)kSecAttrCanSign]; |
[symmetricKeyAttr setObject:(id)kCFBooleanFalse forKey:(id)kSecAttrCanVerify]; |
[symmetricKeyAttr setObject:(id)kCFBooleanFalse forKey:(id)kSecAttrCanWrap]; |
[symmetricKeyAttr setObject:(id)kCFBooleanFalse forKey:(id)kSecAttrCanUnwrap]; |
// Allocate some buffer space. I don't trust calloc. |
symmetricKey = malloc( kChosenCipherKeySize * sizeof(uint8_t) ); |
LOGGING_FACILITY( symmetricKey != NULL, @"Problem allocating buffer space for symmetric key generation." ); |
memset((void *)symmetricKey, 0x0, kChosenCipherKeySize); |
sanityCheck = SecRandomCopyBytes(kSecRandomDefault, kChosenCipherKeySize, symmetricKey); |
LOGGING_FACILITY1( sanityCheck == noErr, @"Problem generating the symmetric key, OSStatus == %d.", sanityCheck ); |
self.symmetricKeyRef = [[NSData alloc] initWithBytes:(const void *)symmetricKey length:kChosenCipherKeySize]; |
// Add the wrapped key data to the container dictionary. |
[symmetricKeyAttr setObject:self.symmetricKeyRef |
forKey:(id)kSecValueData]; |
// Add the symmetric key to the keychain. |
sanityCheck = SecItemAdd((CFDictionaryRef) symmetricKeyAttr, NULL); |
LOGGING_FACILITY1( sanityCheck == noErr || sanityCheck == errSecDuplicateItem, @"Problem storing the symmetric key in the keychain, OSStatus == %d.", sanityCheck ); |
if (symmetricKey) free(symmetricKey); |
[symmetricKeyAttr release]; |
} |
- (SecKeyRef)addPeerPublicKey:(NSString *)peerName keyBits:(NSData *)publicKey { |
OSStatus sanityCheck = noErr; |
SecKeyRef peerKeyRef = NULL; |
CFTypeRef persistPeer = NULL; |
LOGGING_FACILITY( peerName != nil, @"Peer name parameter is nil." ); |
LOGGING_FACILITY( publicKey != nil, @"Public key parameter is nil." ); |
NSData * peerTag = [[NSData alloc] initWithBytes:(const void *)[peerName UTF8String] length:[peerName length]]; |
NSMutableDictionary * peerPublicKeyAttr = [[NSMutableDictionary alloc] init]; |
[peerPublicKeyAttr setObject:(id)kSecClassKey forKey:(id)kSecClass]; |
[peerPublicKeyAttr setObject:(id)kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType]; |
[peerPublicKeyAttr setObject:peerTag forKey:(id)kSecAttrApplicationTag]; |
[peerPublicKeyAttr setObject:publicKey forKey:(id)kSecValueData]; |
[peerPublicKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnPersistentRef]; |
sanityCheck = SecItemAdd((CFDictionaryRef) peerPublicKeyAttr, (CFTypeRef *)&persistPeer); |
// The nice thing about persistent references is that you can write their value out to disk and |
// then use them later. I don't do that here but it certainly can make sense for other situations |
// where you don't want to have to keep building up dictionaries of attributes to get a reference. |
// |
// Also take a look at SecKeyWrapper's methods (CFTypeRef)getPersistentKeyRefWithKeyRef:(SecKeyRef)key |
// & (SecKeyRef)getKeyRefWithPersistentKeyRef:(CFTypeRef)persistentRef. |
LOGGING_FACILITY1( sanityCheck == noErr || sanityCheck == errSecDuplicateItem, @"Problem adding the peer public key to the keychain, OSStatus == %d.", sanityCheck ); |
if (persistPeer) { |
peerKeyRef = [self getKeyRefWithPersistentKeyRef:persistPeer]; |
} else { |
[peerPublicKeyAttr removeObjectForKey:(id)kSecValueData]; |
[peerPublicKeyAttr setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnRef]; |
// Let's retry a different way. |
sanityCheck = SecItemCopyMatching((CFDictionaryRef) peerPublicKeyAttr, (CFTypeRef *)&peerKeyRef); |
} |
LOGGING_FACILITY1( sanityCheck == noErr && peerKeyRef != NULL, @"Problem acquiring reference to the public key, OSStatus == %d.", sanityCheck ); |
[peerTag release]; |
[peerPublicKeyAttr release]; |
if (persistPeer) CFRelease(persistPeer); |
return peerKeyRef; |
} |
- (void)removePeerPublicKey:(NSString *)peerName { |
OSStatus sanityCheck = noErr; |
LOGGING_FACILITY( peerName != nil, @"Peer name parameter is nil." ); |
NSData * peerTag = [[NSData alloc] initWithBytes:(const void *)[peerName UTF8String] length:[peerName length]]; |
NSMutableDictionary * peerPublicKeyAttr = [[NSMutableDictionary alloc] init]; |
[peerPublicKeyAttr setObject:(id)kSecClassKey forKey:(id)kSecClass]; |
[peerPublicKeyAttr setObject:(id)kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType]; |
[peerPublicKeyAttr setObject:peerTag forKey:(id)kSecAttrApplicationTag]; |
sanityCheck = SecItemDelete((CFDictionaryRef) peerPublicKeyAttr); |
LOGGING_FACILITY1( sanityCheck == noErr || sanityCheck == errSecItemNotFound, @"Problem deleting the peer public key to the keychain, OSStatus == %d.", sanityCheck ); |
[peerTag release]; |
[peerPublicKeyAttr release]; |
} |
- (NSData *)wrapSymmetricKey:(NSData *)symmetricKey keyRef:(SecKeyRef)publicKey { |
OSStatus sanityCheck = noErr; |
size_t cipherBufferSize = 0; |
size_t keyBufferSize = 0; |
LOGGING_FACILITY( symmetricKey != nil, @"Symmetric key parameter is nil." ); |
LOGGING_FACILITY( publicKey != nil, @"Key parameter is nil." ); |
NSData * cipher = nil; |
uint8_t * cipherBuffer = NULL; |
// Calculate the buffer sizes. |
cipherBufferSize = SecKeyGetBlockSize(publicKey); |
keyBufferSize = [symmetricKey length]; |
if (kTypeOfWrapPadding == kSecPaddingNone) { |
LOGGING_FACILITY( keyBufferSize <= cipherBufferSize, @"Nonce integer is too large and falls outside multiplicative group." ); |
} else { |
LOGGING_FACILITY( keyBufferSize <= (cipherBufferSize - 11), @"Nonce integer is too large and falls outside multiplicative group." ); |
} |
// Allocate some buffer space. I don't trust calloc. |
cipherBuffer = malloc( cipherBufferSize * sizeof(uint8_t) ); |
memset((void *)cipherBuffer, 0x0, cipherBufferSize); |
// Encrypt using the public key. |
sanityCheck = SecKeyEncrypt( publicKey, |
kTypeOfWrapPadding, |
(const uint8_t *)[symmetricKey bytes], |
keyBufferSize, |
cipherBuffer, |
&cipherBufferSize |
); |
LOGGING_FACILITY1( sanityCheck == noErr, @"Error encrypting, OSStatus == %d.", sanityCheck ); |
// Build up cipher text blob. |
cipher = [NSData dataWithBytes:(const void *)cipherBuffer length:(NSUInteger)cipherBufferSize]; |
if (cipherBuffer) free(cipherBuffer); |
return cipher; |
} |
- (NSData *)unwrapSymmetricKey:(NSData *)wrappedSymmetricKey { |
OSStatus sanityCheck = noErr; |
size_t cipherBufferSize = 0; |
size_t keyBufferSize = 0; |
NSData * key = nil; |
uint8_t * keyBuffer = NULL; |
SecKeyRef privateKey = NULL; |
privateKey = [self getPrivateKeyRef]; |
LOGGING_FACILITY( privateKey != NULL, @"No private key found in the keychain." ); |
// Calculate the buffer sizes. |
cipherBufferSize = SecKeyGetBlockSize(privateKey); |
keyBufferSize = [wrappedSymmetricKey length]; |
LOGGING_FACILITY( keyBufferSize <= cipherBufferSize, @"Encrypted nonce is too large and falls outside multiplicative group." ); |
// Allocate some buffer space. I don't trust calloc. |
keyBuffer = malloc( keyBufferSize * sizeof(uint8_t) ); |
memset((void *)keyBuffer, 0x0, keyBufferSize); |
// Decrypt using the private key. |
sanityCheck = SecKeyDecrypt( privateKey, |
kTypeOfWrapPadding, |
(const uint8_t *) [wrappedSymmetricKey bytes], |
cipherBufferSize, |
keyBuffer, |
&keyBufferSize |
); |
LOGGING_FACILITY1( sanityCheck == noErr, @"Error decrypting, OSStatus == %d.", sanityCheck ); |
// Build up plain text blob. |
key = [NSData dataWithBytes:(const void *)keyBuffer length:(NSUInteger)keyBufferSize]; |
if (keyBuffer) free(keyBuffer); |
return key; |
} |
- (NSData *)getHashBytes:(NSData *)plainText { |
CC_SHA1_CTX ctx; |
uint8_t * hashBytes = NULL; |
NSData * hash = nil; |
// Malloc a buffer to hold hash. |
hashBytes = malloc( kChosenDigestLength * sizeof(uint8_t) ); |
memset((void *)hashBytes, 0x0, kChosenDigestLength); |
// Initialize the context. |
CC_SHA1_Init(&ctx); |
// Perform the hash. |
CC_SHA1_Update(&ctx, (void *)[plainText bytes], [plainText length]); |
// Finalize the output. |
CC_SHA1_Final(hashBytes, &ctx); |
// Build up the SHA1 blob. |
hash = [NSData dataWithBytes:(const void *)hashBytes length:(NSUInteger)kChosenDigestLength]; |
if (hashBytes) free(hashBytes); |
return hash; |
} |
- (NSData *)getSignatureBytes:(NSData *)plainText { |
OSStatus sanityCheck = noErr; |
NSData * signedHash = nil; |
uint8_t * signedHashBytes = NULL; |
size_t signedHashBytesSize = 0; |
SecKeyRef privateKey = NULL; |
privateKey = [self getPrivateKeyRef]; |
signedHashBytesSize = SecKeyGetBlockSize(privateKey); |
// Malloc a buffer to hold signature. |
signedHashBytes = malloc( signedHashBytesSize * sizeof(uint8_t) ); |
memset((void *)signedHashBytes, 0x0, signedHashBytesSize); |
// Sign the SHA1 hash. |
sanityCheck = SecKeyRawSign( privateKey, |
kTypeOfSigPadding, |
(const uint8_t *)[[self getHashBytes:plainText] bytes], |
kChosenDigestLength, |
(uint8_t *)signedHashBytes, |
&signedHashBytesSize |
); |
LOGGING_FACILITY1( sanityCheck == noErr, @"Problem signing the SHA1 hash, OSStatus == %d.", sanityCheck ); |
// Build up signed SHA1 blob. |
signedHash = [NSData dataWithBytes:(const void *)signedHashBytes length:(NSUInteger)signedHashBytesSize]; |
if (signedHashBytes) free(signedHashBytes); |
return signedHash; |
} |
- (BOOL)verifySignature:(NSData *)plainText secKeyRef:(SecKeyRef)publicKey signature:(NSData *)sig { |
size_t signedHashBytesSize = 0; |
OSStatus sanityCheck = noErr; |
// Get the size of the assymetric block. |
signedHashBytesSize = SecKeyGetBlockSize(publicKey); |
sanityCheck = SecKeyRawVerify( publicKey, |
kTypeOfSigPadding, |
(const uint8_t *)[[self getHashBytes:plainText] bytes], |
kChosenDigestLength, |
(const uint8_t *)[sig bytes], |
signedHashBytesSize |
); |
return (sanityCheck == noErr) ? YES : NO; |
} |
- (NSData *)doCipher:(NSData *)plainText key:(NSData *)symmetricKey context:(CCOperation)encryptOrDecrypt padding:(CCOptions *)pkcs7 { |
CCCryptorStatus ccStatus = kCCSuccess; |
// Symmetric crypto reference. |
CCCryptorRef thisEncipher = NULL; |
// Cipher Text container. |
NSData * cipherOrPlainText = nil; |
// Pointer to output buffer. |
uint8_t * bufferPtr = NULL; |
// Total size of the buffer. |
size_t bufferPtrSize = 0; |
// Remaining bytes to be performed on. |
size_t remainingBytes = 0; |
// Number of bytes moved to buffer. |
size_t movedBytes = 0; |
// Length of plainText buffer. |
size_t plainTextBufferSize = 0; |
// Placeholder for total written. |
size_t totalBytesWritten = 0; |
// A friendly helper pointer. |
uint8_t * ptr; |
// Initialization vector; dummy in this case 0's. |
uint8_t iv[kChosenCipherBlockSize]; |
memset((void *) iv, 0x0, (size_t) sizeof(iv)); |
LOGGING_FACILITY(plainText != nil, @"PlainText object cannot be nil." ); |
LOGGING_FACILITY(symmetricKey != nil, @"Symmetric key object cannot be nil." ); |
LOGGING_FACILITY(pkcs7 != NULL, @"CCOptions * pkcs7 cannot be NULL." ); |
LOGGING_FACILITY([symmetricKey length] == kChosenCipherKeySize, @"Disjoint choices for key size." ); |
plainTextBufferSize = [plainText length]; |
LOGGING_FACILITY(plainTextBufferSize > 0, @"Empty plaintext passed in." ); |
// We don't want to toss padding on if we don't need to |
if (encryptOrDecrypt == kCCEncrypt) { |
if (*pkcs7 != kCCOptionECBMode) { |
if ((plainTextBufferSize % kChosenCipherBlockSize) == 0) { |
*pkcs7 = 0x0000; |
} else { |
*pkcs7 = kCCOptionPKCS7Padding; |
} |
} |
} else if (encryptOrDecrypt != kCCDecrypt) { |
LOGGING_FACILITY1( 0, @"Invalid CCOperation parameter [%d] for cipher context.", *pkcs7 ); |
} |
// Create and Initialize the crypto reference. |
ccStatus = CCCryptorCreate( encryptOrDecrypt, |
kCCAlgorithmAES128, |
*pkcs7, |
(const void *)[symmetricKey bytes], |
kChosenCipherKeySize, |
(const void *)iv, |
&thisEncipher |
); |
LOGGING_FACILITY1( ccStatus == kCCSuccess, @"Problem creating the context, ccStatus == %d.", ccStatus ); |
// Calculate byte block alignment for all calls through to and including final. |
bufferPtrSize = CCCryptorGetOutputLength(thisEncipher, plainTextBufferSize, true); |
// Allocate buffer. |
bufferPtr = malloc( bufferPtrSize * sizeof(uint8_t) ); |
// Zero out buffer. |
memset((void *)bufferPtr, 0x0, bufferPtrSize); |
// Initialize some necessary book keeping. |
ptr = bufferPtr; |
// Set up initial size. |
remainingBytes = bufferPtrSize; |
// Actually perform the encryption or decryption. |
ccStatus = CCCryptorUpdate( thisEncipher, |
(const void *) [plainText bytes], |
plainTextBufferSize, |
ptr, |
remainingBytes, |
&movedBytes |
); |
LOGGING_FACILITY1( ccStatus == kCCSuccess, @"Problem with CCCryptorUpdate, ccStatus == %d.", ccStatus ); |
// Handle book keeping. |
ptr += movedBytes; |
remainingBytes -= movedBytes; |
totalBytesWritten += movedBytes; |
// Finalize everything to the output buffer. |
ccStatus = CCCryptorFinal( thisEncipher, |
ptr, |
remainingBytes, |
&movedBytes |
); |
totalBytesWritten += movedBytes; |
if (thisEncipher) { |
(void) CCCryptorRelease(thisEncipher); |
thisEncipher = NULL; |
} |
LOGGING_FACILITY1( ccStatus == kCCSuccess, @"Problem with encipherment ccStatus == %d", ccStatus ); |
cipherOrPlainText = [NSData dataWithBytes:(const void *)bufferPtr length:(NSUInteger)totalBytesWritten]; |
if (bufferPtr) free(bufferPtr); |
return cipherOrPlainText; |
/* |
Or the corresponding one-shot call: |
ccStatus = CCCrypt( encryptOrDecrypt, |
kCCAlgorithmAES128, |
typeOfSymmetricOpts, |
(const void *)[self getSymmetricKeyBytes], |
kChosenCipherKeySize, |
iv, |
(const void *) [plainText bytes], |
plainTextBufferSize, |
(void *)bufferPtr, |
bufferPtrSize, |
&movedBytes |
); |
*/ |
} |
- (SecKeyRef)getPublicKeyRef { |
OSStatus sanityCheck = noErr; |
SecKeyRef publicKeyReference = NULL; |
if (publicKeyRef == NULL) { |
NSMutableDictionary * queryPublicKey = [[NSMutableDictionary alloc] init]; |
// Set the public key query dictionary. |
[queryPublicKey setObject:(id)kSecClassKey forKey:(id)kSecClass]; |
[queryPublicKey setObject:publicTag forKey:(id)kSecAttrApplicationTag]; |
[queryPublicKey setObject:(id)kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType]; |
[queryPublicKey setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnRef]; |
// Get the key. |
sanityCheck = SecItemCopyMatching((CFDictionaryRef)queryPublicKey, (CFTypeRef *)&publicKeyReference); |
if (sanityCheck != noErr) |
{ |
publicKeyReference = NULL; |
} |
[queryPublicKey release]; |
} else { |
publicKeyReference = publicKeyRef; |
} |
return publicKeyReference; |
} |
- (NSData *)getPublicKeyBits { |
OSStatus sanityCheck = noErr; |
NSData * publicKeyBits = nil; |
NSMutableDictionary * queryPublicKey = [[NSMutableDictionary alloc] init]; |
// Set the public key query dictionary. |
[queryPublicKey setObject:(id)kSecClassKey forKey:(id)kSecClass]; |
[queryPublicKey setObject:publicTag forKey:(id)kSecAttrApplicationTag]; |
[queryPublicKey setObject:(id)kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType]; |
[queryPublicKey setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnData]; |
// Get the key bits. |
sanityCheck = SecItemCopyMatching((CFDictionaryRef)queryPublicKey, (CFTypeRef *)&publicKeyBits); |
if (sanityCheck != noErr) |
{ |
publicKeyBits = nil; |
} |
[queryPublicKey release]; |
return publicKeyBits; |
} |
- (SecKeyRef)getPrivateKeyRef { |
OSStatus sanityCheck = noErr; |
SecKeyRef privateKeyReference = NULL; |
if (privateKeyRef == NULL) { |
NSMutableDictionary * queryPrivateKey = [[NSMutableDictionary alloc] init]; |
// Set the private key query dictionary. |
[queryPrivateKey setObject:(id)kSecClassKey forKey:(id)kSecClass]; |
[queryPrivateKey setObject:privateTag forKey:(id)kSecAttrApplicationTag]; |
[queryPrivateKey setObject:(id)kSecAttrKeyTypeRSA forKey:(id)kSecAttrKeyType]; |
[queryPrivateKey setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnRef]; |
// Get the key. |
sanityCheck = SecItemCopyMatching((CFDictionaryRef)queryPrivateKey, (CFTypeRef *)&privateKeyReference); |
if (sanityCheck != noErr) |
{ |
privateKeyReference = NULL; |
} |
[queryPrivateKey release]; |
} else { |
privateKeyReference = privateKeyRef; |
} |
return privateKeyReference; |
} |
- (NSData *)getSymmetricKeyBytes { |
OSStatus sanityCheck = noErr; |
NSData * symmetricKeyReturn = nil; |
if (self.symmetricKeyRef == nil) { |
NSMutableDictionary * querySymmetricKey = [[NSMutableDictionary alloc] init]; |
// Set the private key query dictionary. |
[querySymmetricKey setObject:(id)kSecClassKey forKey:(id)kSecClass]; |
[querySymmetricKey setObject:symmetricTag forKey:(id)kSecAttrApplicationTag]; |
[querySymmetricKey setObject:[NSNumber numberWithUnsignedInt:CSSM_ALGID_AES] forKey:(id)kSecAttrKeyType]; |
[querySymmetricKey setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnData]; |
// Get the key bits. |
sanityCheck = SecItemCopyMatching((CFDictionaryRef)querySymmetricKey, (CFTypeRef *)&symmetricKeyReturn); |
if (sanityCheck == noErr && symmetricKeyReturn != nil) { |
self.symmetricKeyRef = symmetricKeyReturn; |
} else { |
self.symmetricKeyRef = nil; |
} |
[querySymmetricKey release]; |
} else { |
symmetricKeyReturn = self.symmetricKeyRef; |
} |
return symmetricKeyReturn; |
} |
- (CFTypeRef)getPersistentKeyRefWithKeyRef:(SecKeyRef)keyRef { |
OSStatus sanityCheck = noErr; |
CFTypeRef persistentRef = NULL; |
LOGGING_FACILITY(keyRef != NULL, @"keyRef object cannot be NULL." ); |
NSMutableDictionary * queryKey = [[NSMutableDictionary alloc] init]; |
// Set the PersistentKeyRef key query dictionary. |
[queryKey setObject:(id)keyRef forKey:(id)kSecValueRef]; |
[queryKey setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnPersistentRef]; |
// Get the persistent key reference. |
sanityCheck = SecItemCopyMatching((CFDictionaryRef)queryKey, (CFTypeRef *)&persistentRef); |
[queryKey release]; |
return persistentRef; |
} |
- (SecKeyRef)getKeyRefWithPersistentKeyRef:(CFTypeRef)persistentRef { |
OSStatus sanityCheck = noErr; |
SecKeyRef keyRef = NULL; |
LOGGING_FACILITY(persistentRef != NULL, @"persistentRef object cannot be NULL." ); |
NSMutableDictionary * queryKey = [[NSMutableDictionary alloc] init]; |
// Set the SecKeyRef query dictionary. |
[queryKey setObject:(id)persistentRef forKey:(id)kSecValuePersistentRef]; |
[queryKey setObject:[NSNumber numberWithBool:YES] forKey:(id)kSecReturnRef]; |
// Get the persistent key reference. |
sanityCheck = SecItemCopyMatching((CFDictionaryRef)queryKey, (CFTypeRef *)&keyRef); |
[queryKey release]; |
return keyRef; |
} |
- (void)dealloc { |
[privateTag release]; |
[publicTag release]; |
[symmetricTag release]; |
[symmetricKeyRef release]; |
if (publicKeyRef) CFRelease(publicKeyRef); |
if (privateKeyRef) CFRelease(privateKeyRef); |
[super dealloc]; |
} |
#endif |
@end |
Copyright © 2018 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2018-06-04