Updated the README for the 0.1 release.
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_DECRYPT | CSSM_KEYUSE_SIGN, // private key
159 CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_SENSITIVE | CSSM_KEYATTR_PERMANENT,
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(@"%@[%@]", [self class], self.publicKeyDigest.abbreviatedHexString);
178 @synthesize publicKey=_publicKey;
180 - (MYSHA1Digest*) publicKeyDigest {
181 return _publicKey.publicKeyDigest;
184 - (SecExternalItemType) keyType {
185 #if MYCRYPTO_USE_IPHONE_API
186 return kSecAttrKeyClassPublic;
188 return kSecItemTypePrivateKey;
192 - (NSData *) keyData {
193 [NSException raise: NSGenericException format: @"Can't access keyData of a PrivateKey"];
197 - (BOOL) setValue: (NSString*)valueStr ofAttribute: (SecKeychainAttrType)attr {
198 return [super setValue: valueStr ofAttribute: attr]
199 && [_publicKey setValue: valueStr ofAttribute: attr];
204 #pragma mark OPERATIONS:
207 - (BOOL) removeFromKeychain {
208 return [super removeFromKeychain]
209 && [_publicKey removeFromKeychain];
213 - (NSData*) decryptData: (NSData*)data {
214 return [self _crypt: data operation: NO];
218 - (NSData*) signData: (NSData*)data {
220 #if MYCRYPTO_USE_IPHONE_API
221 uint8_t digest[CC_SHA1_DIGEST_LENGTH];
222 CC_SHA1(data.bytes,data.length, digest);
224 size_t sigLen = 1024;
225 uint8_t sigBuf[sigLen];
226 OSStatus err = SecKeyRawSign(self.keyRef, kSecPaddingPKCS1SHA1,
227 digest,sizeof(digest), //data.bytes, data.length,
230 Warn(@"SecKeyRawSign failed: %i",err);
233 return [NSData dataWithBytes: sigBuf length: sigLen];
235 NSData *signature = nil;
236 CSSM_CC_HANDLE ccHandle = [self _createSignatureContext: CSSM_ALGID_SHA256WithRSA];
237 if (!ccHandle) return nil;
238 CSSM_DATA original = {data.length, (void*)data.bytes};
239 CSSM_DATA result = {0,NULL};
240 if (checkcssm(CSSM_SignData(ccHandle, &original, 1, CSSM_ALGID_NONE, &result), @"CSSM_SignData"))
241 signature = [NSData dataWithBytesNoCopy: result.Data length: result.Length
243 CSSM_DeleteContext(ccHandle);
249 #if !TARGET_OS_IPHONE
251 - (NSData*) exportKeyInFormat: (SecExternalFormat)format
252 withPEM: (BOOL)withPEM
253 alertTitle: (NSString*)title
254 alertPrompt: (NSString*)prompt
256 SecKeyImportExportParameters params = {
257 .version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION,
258 .flags = kSecKeySecurePassphrase,
259 .alertTitle = (CFStringRef)title,
260 .alertPrompt = (CFStringRef)prompt
262 CFDataRef data = NULL;
263 if (check(SecKeychainItemExport(self.keyRef,
264 format, (withPEM ?kSecItemPemArmour :0),
266 @"SecKeychainItemExport"))
267 return [(id)CFMakeCollectable(data) autorelease];
272 - (NSData*) exportKey {
273 return [self exportKeyInFormat: kSecFormatWrappedOpenSSL withPEM: YES
274 alertTitle: @"Export Private Key"
275 alertPrompt: @"Enter a passphrase to protect the private-key file.\n"
276 "You will need to re-enter the passphrase later when importing the key from this file, "
277 "so keep it in a safe place."];
278 //FIX: Should make these messages localizable.
282 - (NSData*) _exportKeyInFormat: (SecExternalFormat)format
283 withPEM: (BOOL)withPEM
284 passphrase: (NSString*)passphrase
286 SecKeyImportExportParameters params = {
287 .version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION,
288 .passphrase = (CFStringRef)passphrase
290 CFDataRef data = NULL;
291 if (check(SecKeychainItemExport(self.keyRef,
292 format, (withPEM ?kSecItemPemArmour :0),
294 @"SecKeychainItemExport"))
295 return [(id)CFMakeCollectable(data) autorelease];
300 #endif TARGET_OS_IPHONE