5 // Created by Jens Alfke on 4/7/09.
6 // Copyright 2009 Jens Alfke. All rights reserved.
9 #import "MYPrivateKey.h"
10 #import "MYCrypto_Private.h"
12 #import <CommonCrypto/CommonDigest.h>
18 @implementation MYPrivateKey
21 - (id) initWithKeyRef: (SecKeyRef)privateKey
23 self = [super initWithKeyRef: privateKey];
25 // No public key given, so look it up:
26 MYSHA1Digest *digest = self._keyDigest;
28 _publicKey = [[self.keychain publicKeyWithDigest: digest] retain];
30 // The matching public key won't turn up if it's embedded in a certificate;
31 // I'd have to search for certs if I wanted to look that up. Skip it for now.
32 Log(@"MYPrivateKey(%p): Couldn't find matching public key for private key! digest=%@",
42 - (id) _initWithKeyRef: (SecKeyRef)privateKey
43 publicKey: (MYPublicKey*)publicKey
46 self = [super initWithKeyRef: privateKey];
48 _publicKey = [publicKey retain];
53 - (id) initWithKeyRef: (SecKeyRef)privateKey
54 publicKeyRef: (SecKeyRef)publicKeyRef
56 MYPublicKey *publicKey = [[MYPublicKey alloc] initWithKeyRef: publicKeyRef];
57 self = [self _initWithKeyRef: privateKey publicKey: publicKey];
62 - (id) _initWithKeyRef: (SecKeyRef)privateKey
63 publicKeyData: (NSData*)pubKeyData
64 forKeychain: (SecKeychainRef)keychain
70 MYPublicKey *pubKey = [[MYPublicKey alloc] _initWithKeyData: pubKeyData forKeychain: keychain];
75 self = [super initWithKeyRef: privateKey];
79 [pubKey removeFromKeychain];
88 // The public API for this is in MYKeychain.
89 - (id) _initWithKeyData: (NSData*)privKeyData
90 publicKeyData: (NSData*)pubKeyData
91 forKeychain: (SecKeychainRef)keychain
92 alertTitle: (NSString*)title
93 alertPrompt: (NSString*)prompt
95 // Try to import the private key first, since the user might cancel the passphrase alert.
96 SecKeyImportExportParameters params = {
97 .flags = kSecKeySecurePassphrase,
98 .alertTitle = (CFStringRef) title,
99 .alertPrompt = (CFStringRef) prompt
101 SecKeyRef privateKey = importKey(privKeyData,kSecItemTypePrivateKey,keychain,¶ms);
102 return [self _initWithKeyRef: privateKey publicKeyData: pubKeyData forKeychain: keychain];
105 // This method is for testing, so unit-tests don't require user intervention.
106 // It's deliberately not made public, to discourage clients from trying to manage the passphrases
107 // themselves (this is less secure than letting the Security agent do it.)
108 - (id) _initWithKeyData: (NSData*)privKeyData
109 publicKeyData: (NSData*)pubKeyData
110 forKeychain: (SecKeychainRef)keychain
111 passphrase: (NSString*)passphrase
113 SecKeyImportExportParameters params = {
114 .passphrase = (CFStringRef) passphrase,
116 SecKeyRef privateKey = importKey(privKeyData,kSecItemTypePrivateKey,keychain,¶ms);
117 return [self _initWithKeyRef: privateKey publicKeyData: pubKeyData forKeychain: keychain];
121 - (MYIdentity*) createSelfSignedIdentityWithAttributes: (NSDictionary*)attributes {
122 return MYIdentityCreateSelfSigned(self, attributes);
131 [_publicKey release];
136 + (MYPrivateKey*) _generateRSAKeyPairOfSize: (unsigned)keySize
137 inKeychain: (MYKeychain*)keychain
139 Assert( keySize == 512 || keySize == 1024 || keySize == 2048, @"Unsupported key size %u", keySize );
140 SecKeyRef pubKey=NULL, privKey=NULL;
143 #if MYCRYPTO_USE_IPHONE_API
144 NSDictionary *pubKeyAttrs = $dict({(id)kSecAttrIsPermanent, $true});
145 NSDictionary *privKeyAttrs = $dict({(id)kSecAttrIsPermanent, $true});
146 NSDictionary *keyAttrs = $dict( {(id)kSecAttrKeyType, (id)kSecAttrKeyTypeRSA},
147 {(id)kSecAttrKeySizeInBits, $object(keySize)},
148 {(id)kSecPublicKeyAttrs, pubKeyAttrs},
149 {(id)kSecPrivateKeyAttrs, privKeyAttrs} );
150 err = SecKeyGeneratePair((CFDictionaryRef)keyAttrs,&pubKey,&privKey);
152 err = SecKeyCreatePair(keychain.keychainRefOrDefault,
156 CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_VERIFY, // public key
157 CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_PERMANENT,
158 CSSM_KEYUSE_ANY, // private key
159 CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_SENSITIVE,
160 NULL, // SecAccessRef
163 if (!check(err, @"SecKeyCreatePair")) {
166 return [[[self alloc] initWithKeyRef: privKey publicKeyRef: pubKey] autorelease];
171 #pragma mark ACCESSORS:
174 - (NSString*) description {
175 return $sprintf(@"%@[%@ %@ /%p]", [self class],
176 self.publicKeyDigest.abbreviatedHexString,
178 self.keychainItemRef);
181 @synthesize publicKey=_publicKey;
183 - (MYSHA1Digest*) publicKeyDigest {
184 return _publicKey.publicKeyDigest;
187 - (SecExternalItemType) keyType {
188 #if MYCRYPTO_USE_IPHONE_API
189 return kSecAttrKeyClassPublic;
191 return kSecItemTypePrivateKey;
195 - (NSData *) keyData {
196 [NSException raise: NSGenericException format: @"Can't access keyData of a PrivateKey"];
200 - (BOOL) setValue: (NSString*)valueStr ofAttribute: (SecKeychainAttrType)attr {
201 return [super setValue: valueStr ofAttribute: attr]
202 && [_publicKey setValue: valueStr ofAttribute: attr];
207 #pragma mark OPERATIONS:
210 - (BOOL) removeFromKeychain {
211 return [super removeFromKeychain]
212 && [_publicKey removeFromKeychain];
216 - (NSData*) decryptData: (NSData*)data {
217 return [self _crypt: data operation: NO];
221 - (NSData*) signData: (NSData*)data {
223 #if MYCRYPTO_USE_IPHONE_API
224 uint8_t digest[CC_SHA1_DIGEST_LENGTH];
225 CC_SHA1(data.bytes,data.length, digest);
227 size_t sigLen = 1024;
228 uint8_t sigBuf[sigLen];
229 OSStatus err = SecKeyRawSign(self.keyRef, kSecPaddingPKCS1SHA1,
230 digest,sizeof(digest), //data.bytes, data.length,
233 Warn(@"SecKeyRawSign failed: %i",err);
236 return [NSData dataWithBytes: sigBuf length: sigLen];
238 NSData *signature = nil;
239 CSSM_CC_HANDLE ccHandle = [self _createSignatureContext: CSSM_ALGID_SHA256WithRSA];
240 if (!ccHandle) return nil;
241 CSSM_DATA original = {data.length, (void*)data.bytes};
242 CSSM_DATA result = {0,NULL};
243 if (checkcssm(CSSM_SignData(ccHandle, &original, 1, CSSM_ALGID_NONE, &result), @"CSSM_SignData"))
244 signature = [NSData dataWithBytesNoCopy: result.Data length: result.Length
246 CSSM_DeleteContext(ccHandle);
252 #if !TARGET_OS_IPHONE
254 - (NSData*) exportKeyInFormat: (SecExternalFormat)format
255 withPEM: (BOOL)withPEM
256 alertTitle: (NSString*)title
257 alertPrompt: (NSString*)prompt
259 SecKeyImportExportParameters params = {
260 .version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION,
261 .flags = kSecKeySecurePassphrase,
262 .alertTitle = (CFStringRef)title,
263 .alertPrompt = (CFStringRef)prompt
265 CFDataRef data = NULL;
266 if (check(SecKeychainItemExport(self.keyRef,
267 format, (withPEM ?kSecItemPemArmour :0),
269 @"SecKeychainItemExport"))
270 return [(id)CFMakeCollectable(data) autorelease];
275 - (NSData*) exportKey {
276 return [self exportKeyInFormat: kSecFormatWrappedOpenSSL withPEM: YES
277 alertTitle: @"Export Private Key"
278 alertPrompt: @"Enter a passphrase to protect the private-key file.\n"
279 "You will need to re-enter the passphrase later when importing the key from this file, "
280 "so keep it in a safe place."];
281 //FIX: Should make these messages localizable.
285 - (NSData*) _exportKeyInFormat: (SecExternalFormat)format
286 withPEM: (BOOL)withPEM
287 passphrase: (NSString*)passphrase
289 SecKeyImportExportParameters params = {
290 .version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION,
291 .passphrase = (CFStringRef)passphrase
293 CFDataRef data = NULL;
294 if (check(SecKeychainItemExport(self.keyRef,
295 format, (withPEM ?kSecItemPemArmour :0),
297 @"SecKeychainItemExport"))
298 return [(id)CFMakeCollectable(data) autorelease];
303 #endif TARGET_OS_IPHONE