* 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.
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];
126 [_publicKey release];
131 + (MYPrivateKey*) _generateRSAKeyPairOfSize: (unsigned)keySize
132 inKeychain: (MYKeychain*)keychain
134 Assert( keySize == 512 || keySize == 1024 || keySize == 2048, @"Unsupported key size %u", keySize );
135 SecKeyRef pubKey=NULL, privKey=NULL;
138 #if MYCRYPTO_USE_IPHONE_API
139 NSDictionary *pubKeyAttrs = $dict({(id)kSecAttrIsPermanent, $true});
140 NSDictionary *privKeyAttrs = $dict({(id)kSecAttrIsPermanent, $true});
141 NSDictionary *keyAttrs = $dict( {(id)kSecAttrKeyType, (id)kSecAttrKeyTypeRSA},
142 {(id)kSecAttrKeySizeInBits, $object(keySize)},
143 {(id)kSecPublicKeyAttrs, pubKeyAttrs},
144 {(id)kSecPrivateKeyAttrs, privKeyAttrs} );
145 err = SecKeyGeneratePair((CFDictionaryRef)keyAttrs,&pubKey,&privKey);
147 err = SecKeyCreatePair(keychain.keychainRefOrDefault,
151 CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_VERIFY | CSSM_KEYUSE_WRAP, // public key
152 CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_PERMANENT,
153 CSSM_KEYUSE_ANY, // private key
154 CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_SENSITIVE,
155 NULL, // SecAccessRef
158 if (!check(err, @"SecKeyCreatePair")) {
161 return [[[self alloc] initWithKeyRef: privKey publicKeyRef: pubKey] autorelease];
166 #pragma mark ACCESSORS:
169 - (NSString*) description {
170 return $sprintf(@"%@[%@ %@ /%p]", [self class],
171 self.publicKeyDigest.abbreviatedHexString,
173 self.keychainItemRef);
176 @synthesize publicKey=_publicKey;
178 - (MYSHA1Digest*) publicKeyDigest {
179 return _publicKey.publicKeyDigest;
182 - (SecExternalItemType) keyType {
183 #if MYCRYPTO_USE_IPHONE_API
184 return kSecAttrKeyClassPublic;
186 return kSecItemTypePrivateKey;
190 - (NSData *) keyData {
191 [NSException raise: NSGenericException format: @"Can't access keyData of a PrivateKey"];
195 - (BOOL) setValue: (NSString*)valueStr ofAttribute: (SecKeychainAttrType)attr {
196 return [super setValue: valueStr ofAttribute: attr]
197 && [_publicKey setValue: valueStr ofAttribute: attr];
202 #pragma mark OPERATIONS:
205 - (BOOL) removeFromKeychain {
206 return [super removeFromKeychain]
207 && [_publicKey removeFromKeychain];
211 - (NSData*) rawDecryptData: (NSData*)data {
212 return [self _crypt: data operation: NO];
216 - (NSData*) signData: (NSData*)data {
218 #if MYCRYPTO_USE_IPHONE_API
219 uint8_t digest[CC_SHA1_DIGEST_LENGTH];
220 CC_SHA1(data.bytes,data.length, digest);
222 size_t sigLen = 1024;
223 uint8_t sigBuf[sigLen];
224 OSStatus err = SecKeyRawSign(self.keyRef, kSecPaddingPKCS1SHA1,
225 digest,sizeof(digest), //data.bytes, data.length,
228 Warn(@"SecKeyRawSign failed: %i",err);
231 return [NSData dataWithBytes: sigBuf length: sigLen];
233 NSData *signature = nil;
234 CSSM_CC_HANDLE ccHandle = [self _createSignatureContext: CSSM_ALGID_SHA1WithRSA];
235 if (!ccHandle) return nil;
236 CSSM_DATA original = {data.length, (void*)data.bytes};
237 CSSM_DATA result = {0,NULL};
238 if (checkcssm(CSSM_SignData(ccHandle, &original, 1, CSSM_ALGID_NONE, &result), @"CSSM_SignData"))
239 signature = [NSData dataWithBytesNoCopy: result.Data length: result.Length
241 CSSM_DeleteContext(ccHandle);
247 #if !TARGET_OS_IPHONE
249 - (NSData*) exportKeyInFormat: (SecExternalFormat)format
250 withPEM: (BOOL)withPEM
251 alertTitle: (NSString*)title
252 alertPrompt: (NSString*)prompt
254 SecKeyImportExportParameters params = {
255 .version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION,
256 .flags = kSecKeySecurePassphrase,
257 .alertTitle = (CFStringRef)title,
258 .alertPrompt = (CFStringRef)prompt
260 CFDataRef data = NULL;
261 if (check(SecKeychainItemExport(self.keyRef,
262 format, (withPEM ?kSecItemPemArmour :0),
264 @"SecKeychainItemExport"))
265 return [(id)CFMakeCollectable(data) autorelease];
270 - (NSData*) exportKey {
271 return [self exportKeyInFormat: kSecFormatWrappedOpenSSL withPEM: YES
272 alertTitle: @"Export Private Key"
273 alertPrompt: @"Enter a passphrase to protect the private-key file.\n"
274 "You will need to re-enter the passphrase later when importing the key from this file, "
275 "so keep it in a safe place."];
276 //FIX: Should make these messages localizable.
280 - (NSData*) _exportKeyInFormat: (SecExternalFormat)format
281 withPEM: (BOOL)withPEM
282 passphrase: (NSString*)passphrase
284 SecKeyImportExportParameters params = {
285 .version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION,
286 .passphrase = (CFStringRef)passphrase
288 CFDataRef data = NULL;
289 if (check(SecKeychainItemExport(self.keyRef,
290 format, (withPEM ?kSecItemPemArmour :0),
292 @"SecKeychainItemExport"))
293 return [(id)CFMakeCollectable(data) autorelease];
299 - (MYSymmetricKey*) unwrapSessionKey: (NSData*)wrappedData
300 withAlgorithm: (CCAlgorithm)algorithm
301 sizeInBits: (unsigned)sizeInBits
303 // First create a wrapped-key structure from the data:
304 CSSM_WRAP_KEY wrappedKey = {
306 .BlobType = CSSM_KEYBLOB_WRAPPED,
307 .Format = CSSM_KEYBLOB_RAW_FORMAT_PKCS3,
308 .AlgorithmId = CSSMFromCCAlgorithm(algorithm),
309 .KeyClass = CSSM_KEYCLASS_SESSION_KEY,
310 .LogicalKeySizeInBits = sizeInBits,
311 .KeyAttr = CSSM_KEYATTR_EXTRACTABLE,
312 .KeyUsage = CSSM_KEYUSE_ANY,
313 .WrapAlgorithmId = self.cssmAlgorithm,
316 .Data = (void*)wrappedData.bytes,
317 .Length = wrappedData.length
321 const CSSM_ACCESS_CREDENTIALS* credentials;
322 credentials = [self cssmCredentialsForOperation: CSSM_ACL_AUTHORIZATION_IMPORT_WRAPPED
323 type: kSecCredentialTypeDefault error: nil];
324 CSSM_CSP_HANDLE cspHandle = self.cssmCSPHandle;
326 if (!checkcssm(CSSM_CSP_CreateAsymmetricContext(cspHandle,
332 @"CSSM_CSP_CreateAsymmetricContext"))
335 // Now unwrap the key:
336 MYSymmetricKey *result = nil;
337 CSSM_KEY *unwrappedKey = calloc(1,sizeof(CSSM_KEY));
338 CSSM_DATA label = {.Data=(void*)"Imported key", .Length=strlen("Imported key")};
339 CSSM_DATA descriptiveData = {};
340 if (checkcssm(CSSM_UnwrapKey(ctx,
343 wrappedKey.KeyHeader.KeyUsage,
344 wrappedKey.KeyHeader.KeyAttr,
349 @"CSSM_UnwrapKey")) {
350 result = [[[MYSymmetricKey alloc] _initWithCSSMKey: unwrappedKey] autorelease];
352 // Finally, delete the context
355 CSSM_DeleteContext(ctx);
360 #endif !TARGET_OS_IPHONE
367 Copyright (c) 2009, Jens Alfke <jens@mooseyard.com>. All rights reserved.
369 Redistribution and use in source and binary forms, with or without modification, are permitted
370 provided that the following conditions are met:
372 * Redistributions of source code must retain the above copyright notice, this list of conditions
373 and the following disclaimer.
374 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
375 and the following disclaimer in the documentation and/or other materials provided with the
378 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
379 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
380 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
381 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
382 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
383 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
384 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
385 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.