MYCryptoTest.m
author Jens Alfke <jens@mooseyard.com>
Tue Jul 21 10:13:08 2009 -0700 (2009-07-21)
changeset 27 d0aadddb9c64
parent 23 39fec79de6e8
permissions -rw-r--r--
MYCertificate now checks validity of self-signed certs loaded from the keychain (because the Security framework doesn't validate self-signed certs.)
     1 //
     2 //  MYCryptoTest.m
     3 //  MYCrypto-iPhone
     4 //
     5 //  Created by Jens Alfke on 4/1/09.
     6 //  Copyright 2009 Jens Alfke. All rights reserved.
     7 //
     8 
     9 #import "MYPublicKey.h"
    10 #import "MYPrivateKey.h"
    11 #import "MYKeychain.h"
    12 #import "MYDigest.h"
    13 #import "MYIdentity.h"
    14 #if !TARGET_OS_IPHONE
    15 #import "MYCrypto+Cocoa.h"
    16 #endif
    17 #import "MYCrypto_Private.h"
    18 
    19 
    20 #if DEBUG
    21 
    22 
    23 #define kTestCaseRSAKeySize 2048
    24 
    25 #pragma mark -
    26 #pragma mark KEYCHAIN:
    27 
    28 
    29 TestCase(MYKeychain) {
    30     MYKeychain *kc = [MYKeychain defaultKeychain];
    31     Log(@"Default keychain = %@", kc);
    32     CAssert(kc);
    33 #if !MYCRYPTO_USE_IPHONE_API
    34     CAssert(kc.path);
    35 #endif
    36     
    37     kc = [MYKeychain allKeychains];
    38     Log(@"All-keychains = %@", kc);
    39     CAssert(kc);
    40 #if !MYCRYPTO_USE_IPHONE_API
    41     CAssertEq(kc.path,nil);
    42 #endif
    43 }
    44 
    45 
    46 #if MYCRYPTO_USE_IPHONE_API
    47 TestCase(RemoveAll) {
    48     RequireTestCase(MYKeychain);
    49     MYKeychain *kc = [MYKeychain defaultKeychain];
    50     CAssert([kc removeAllCertificates]);
    51     CAssert([kc removeAllKeys]);
    52 }
    53 #endif
    54 
    55 
    56 TestCase(Enumerate) {
    57     RequireTestCase(EnumeratePublicKeys);
    58     RequireTestCase(EnumeratePrivateKeys);
    59     RequireTestCase(EnumerateSymmetricKeys);
    60     RequireTestCase(EnumerateCerts);
    61     RequireTestCase(EnumerateIdentities);
    62 }
    63 
    64 
    65 TestCase(EnumeratePublicKeys) {
    66     RequireTestCase(MYKeychain);
    67     NSEnumerator *e = [[MYKeychain allKeychains] enumeratePublicKeys];
    68     Log(@"Public Key Enumerator = %@", e);
    69     CAssert(e);
    70     for (MYPublicKey *key in e) {
    71         Log(@"Trying public key %@", key.keyRef);
    72         @try{
    73             Log(@"Found %@ -- name=%@", key, key.name);
    74         }@catch (NSException *x) {
    75             Warn(@"Caught %@",x); //TEMP
    76         }
    77     }
    78 }
    79 
    80 TestCase(EnumeratePrivateKeys) {
    81     RequireTestCase(MYKeychain);
    82     NSEnumerator *e = [[MYKeychain allKeychains] enumeratePrivateKeys];
    83     Log(@"Key-Pair Enumerator = %@", e);
    84     CAssert(e);
    85     for (MYPrivateKey *key in e) {
    86         Log(@"Found %@ -- name=%@ --> %@", key, key.name, key.publicKey);
    87         CAssert(key.publicKey);
    88     }
    89 }
    90 
    91 TestCase(EnumerateSymmetricKeys) {
    92     RequireTestCase(MYKeychain);
    93     NSEnumerator *e = [[MYKeychain allKeychains] enumerateSymmetricKeys];
    94     Log(@"Symmetric Key Enumerator = %@", e);
    95     CAssert(e);
    96     for (MYSymmetricKey *key in e) {
    97         Log(@"Found %@ -- name=%@", key, key.name);
    98     }
    99 }
   100 
   101 
   102 TestCase(EnumerateCerts) {
   103     RequireTestCase(MYKeychain);
   104     NSEnumerator *e = [[MYKeychain allKeychains] enumerateCertificates];
   105     Log(@"Enumerator = %@", e);
   106     CAssert(e);
   107     for (MYCertificate *cert in e) {
   108         Log(@"Found %@ -- key=%@, name=%@, email=%@", cert,
   109             cert.publicKey, cert.commonName, cert.emailAddresses);
   110         CAssert(cert.publicKey);
   111         
   112 #if MYCRYPTO_USE_IPHONE_API
   113         // Verify that cert has public-key-hash attribute:
   114         NSData *digestData = [MYKeychainItem _getAttribute: kSecAttrPublicKeyHash 
   115                                                     ofItem: cert.certificateRef];
   116         CAssert(digestData, @"SecCertificateRef missing kSecAttrPublicKeyHash");
   117         MYSHA1Digest *digest = [MYSHA1Digest digestFromDigestData: digestData];
   118         CAssertEqual(digest, cert.publicKey.publicKeyDigest);
   119         Log(@"kSecAttrPublicKeyHash matches: %@", digest);
   120 #else
   121         MYSHA1Digest *digest = cert.publicKey.publicKeyDigest;
   122 #endif
   123         // Verify that certificateWithDigest will find it:
   124         MYCertificate *cert2 = [[MYKeychain allKeychains] certificateWithDigest: digest];
   125         Log(@"certificateWithDigest(%@) returned %@", digest,cert2);
   126         CAssert(cert2);
   127         CAssertEqual(cert2.certificateData, cert.certificateData);
   128     }
   129 }
   130 
   131 TestCase(EnumerateIdentities) {
   132     RequireTestCase(MYKeychain);
   133     NSEnumerator *e = [[MYKeychain allKeychains] enumerateIdentities];
   134     Log(@"Enumerator = %@", e);
   135     CAssert(e);
   136     for (MYIdentity *ident in e) {
   137         Log(@"Found %@\n\tcommonName=%@\n\temails=(%@)\n\tkey=%@",
   138             ident, ident.commonName, 
   139             [ident.emailAddresses componentsJoinedByString: @", "],
   140             ident.privateKey);
   141         
   142         // Verify that identityWithDigest will find it:
   143         MYSHA1Digest *digest = ident.publicKey.publicKeyDigest;
   144         MYIdentity *ident2 = [[MYKeychain allKeychains] identityWithDigest:digest];
   145         Log(@"identityWithDigest(%@) returned %@", digest,ident2);
   146         CAssert(ident2);
   147         CAssertEqual(ident2.certificateData, ident.certificateData);
   148     }
   149 }
   150 
   151 
   152 #pragma mark -
   153 #pragma mark SYMMETRIC KEYS:
   154 
   155 
   156 static void testSymmetricKey( CCAlgorithm algorithm, unsigned sizeInBits, MYKeychain *inKeychain ) {
   157     NSAutoreleasePool *pool = [NSAutoreleasePool new];
   158     MYSymmetricKey *key = nil;
   159     @try{
   160         Log(@"--- Testing %3u-bit #%i %s", sizeInBits, (int)algorithm,
   161             (inKeychain ?", in keychain" :""));
   162         // Generate key:
   163         if (inKeychain)
   164             key = [inKeychain generateSymmetricKeyOfSize: sizeInBits algorithm: algorithm];
   165         else
   166             key = [MYSymmetricKey generateSymmetricKeyOfSize: sizeInBits algorithm: algorithm];
   167         Log(@"Created %@", key);
   168         CAssert(key);
   169         CAssertEq(key.algorithm, algorithm);
   170         CAssertEq(key.keySizeInBits, sizeInBits);
   171     #if !TARGET_OS_IPHONE
   172         CAssert(key.cssmKey != NULL);
   173     #endif
   174         
   175         NSData *keyData = key.keyData;
   176         Log(@"Key data = %@", keyData);
   177         CAssertEq(keyData.length, sizeInBits/8);
   178         
   179         // Encrypt a small amount of text:
   180         Log(@"Testing encryption / decryption ...");
   181         NSData *cleartext = [@"This is a test. This is only a test." dataUsingEncoding: NSUTF8StringEncoding];
   182         NSData *encrypted = [key encryptData: cleartext];
   183         Log(@"Encrypted = %u bytes: %@", encrypted.length, encrypted);
   184         CAssert(encrypted.length >= cleartext.length);
   185         NSData *decrypted = [key decryptData: encrypted];
   186         CAssertEqual(decrypted, cleartext);
   187         
   188         // Encrypt large binary data:
   189         cleartext = [NSData dataWithContentsOfFile: @"/Library/Desktop Pictures/Nature/Zen Garden.jpg"];
   190         CAssert(cleartext);
   191         encrypted = [key encryptData: cleartext];
   192         Log(@"Encrypted = %u bytes", encrypted.length);
   193         CAssert(encrypted.length >= cleartext.length);
   194         decrypted = [key decryptData: encrypted];
   195         CAssertEqual(decrypted, cleartext);
   196         
   197     #if 1
   198         Log(@"Testing initWithKeyData:...");
   199         MYSymmetricKey *key2 = [[MYSymmetricKey alloc] initWithKeyData: keyData algorithm: algorithm];
   200         CAssert(key2);
   201         Log(@"Key from data = %@",key2);
   202         CAssertEqual(key2.keyData, keyData);
   203         CAssertEq(key2.algorithm, algorithm);
   204         CAssertEq(key2.keySizeInBits, sizeInBits);
   205         decrypted = [key2 decryptData: encrypted];
   206         CAssertEqual(decrypted, cleartext);
   207         [key2 release];
   208     #endif
   209 
   210     #if !TARGET_OS_IPHONE
   211         // Try exporting and importing a wrapped key:
   212         Log(@"Testing export/import...");
   213         NSData *exported = [key exportWrappedKeyWithPassphrasePrompt: @"Export symmetric key with passphrase:"];
   214         Log(@"Exported key: %@", exported);
   215     #if 1
   216         CAssert(exported);
   217     #else
   218         if (!exported)
   219             Warn(@"Unable to export wrapped key");
   220         else
   221     #endif
   222         {
   223             CAssert(exported);
   224             MYSymmetricKey *key2 = [[MYSymmetricKey alloc] initWithWrappedKeyData: exported];
   225             Log(@"Reconstituted as %@", key2);
   226             CAssertEqual(key2.keyData,key.keyData);
   227             decrypted = [key2 decryptData: encrypted];
   228             CAssertEqual(decrypted, cleartext);
   229         }
   230     #endif
   231     }@finally{
   232         [key removeFromKeychain];
   233     }
   234     [pool drain];
   235 }
   236 
   237 
   238 TestCase(MYSymmetricKey) {
   239     #define kNTests 11
   240     static const CCAlgorithm kTestAlgorithms[kNTests] = {
   241         kCCAlgorithmAES128, kCCAlgorithmAES128, kCCAlgorithmAES128,
   242         kCCAlgorithmDES, kCCAlgorithm3DES,
   243         kCCAlgorithmCAST, kCCAlgorithmCAST, kCCAlgorithmCAST,
   244         kCCAlgorithmRC4, kCCAlgorithmRC4, kCCAlgorithmRC4};
   245     
   246     static const unsigned kTestBitSizes[kNTests] = {
   247         128, 192, 256,
   248         64, 3*64,
   249         40, 80, 128,
   250         32, 200, 512*8};
   251 
   252     for (int useKeychain=0; useKeychain<=1; useKeychain++)
   253         for (int testNo=0; testNo<kNTests; testNo++) 
   254             testSymmetricKey(kTestAlgorithms[testNo], 
   255                              kTestBitSizes[testNo],
   256                              useKeychain ?[MYKeychain defaultKeychain] :nil);
   257 }
   258 
   259 
   260 #if !TARGET_OS_IPHONE
   261 TestCase(MYSymmetricKeyPassphrase) {
   262     Log(@"Prompting for raw passphrase --");
   263     NSString *rawPassphrase = [MYSymmetricKey promptForPassphraseWithAlertTitle: @"Raw Passphrase Test" 
   264                                                                     alertPrompt: @"Enter the passphrase 'Testing':"
   265                                                                        creating: YES];
   266     Log(@"You entered: '%@'", rawPassphrase);
   267     CAssertEqual(rawPassphrase, @"Testing");
   268     
   269     Log(@"Prompting for passphrase for key --");
   270     MYSymmetricKey *key = [MYSymmetricKey generateFromUserPassphraseWithAlertTitle: @"Symmetric Key Passphrase Test Case" 
   271                                                                        alertPrompt: @"Please enter a passphrase to generate a key:"
   272                                                                           creating: YES
   273                                                                               salt: @"wahooma"];
   274     Log(@"Key from passphrase = %@", key);
   275     CAssert(key);
   276 
   277     // Encrypt a small amount of text:
   278     Log(@"Testing encryption / decryption ...");
   279     NSData *cleartext = [@"This is a test. This is only a test." dataUsingEncoding: NSUTF8StringEncoding];
   280     NSData *encrypted = [key encryptData: cleartext];
   281     Log(@"Encrypted = %u bytes: %@", encrypted.length, encrypted);
   282     CAssert(encrypted.length >= cleartext.length);
   283     NSData *decrypted = [key decryptData: encrypted];
   284     CAssertEqual(decrypted, cleartext);
   285     
   286     // Now test decryption by re-entered passphrase:
   287     Log(@"Testing decryption using re-entered passphrase...");
   288     MYSymmetricKey *key2 = [MYSymmetricKey generateFromUserPassphraseWithAlertTitle: @"Symmetric Key Passphrase Test Case" 
   289                                                                         alertPrompt: @"Please re-enter the same passphrase:" 
   290                                                                            creating: NO
   291                                                                                salt: @"wahooma"];
   292     Log(@"Key from passphrase = %@", key2);
   293     CAssert(key2);
   294     decrypted = [key2 decryptData: encrypted];
   295     CAssertEqual(decrypted, cleartext);
   296 }
   297 #endif
   298 
   299 
   300 #pragma mark -
   301 #pragma mark KEY-PAIRS:
   302 
   303 
   304 static void TestUseKeyPair(MYPrivateKey *pair) {
   305     Log(@"---- TestUseKeyPair { %@ , %@ }.", pair, pair.publicKey);
   306     CAssert(pair);
   307     CAssert(pair.keyRef);
   308     MYPublicKey *publicKey = pair.publicKey;
   309     CAssert(publicKey.keyRef);
   310     
   311     NSData *pubKeyData = publicKey.keyData;
   312     Log(@"Public key = %@ (%u bytes)",pubKeyData,pubKeyData.length);
   313     CAssert(pubKeyData);
   314     
   315     NSData *modulus;
   316     unsigned exponent;
   317     CAssert([publicKey getModulus: &modulus exponent: &exponent]);
   318     Log(@"Modulus = %@", modulus);
   319     Log(@"Exponent = %u", exponent);
   320     CAssertEq(modulus.length, 2048U/8);
   321     CAssertEq(exponent,65537U); // this is what CDSA always seems to use
   322     
   323     MYSHA1Digest *pubKeyDigest = publicKey.publicKeyDigest;
   324     Log(@"Public key digest = %@",pubKeyDigest);
   325     CAssertEqual(pair.publicKeyDigest, pubKeyDigest);
   326     
   327     Log(@"SHA1 of pub key = %@", pubKeyData.my_SHA1Digest.asData);
   328     CAssertEqual(pubKeyData.my_SHA1Digest,pubKeyDigest);
   329     
   330     // Let's sign data:
   331     NSData *data = [@"This is a test. This is only a test!" dataUsingEncoding: NSUTF8StringEncoding];
   332     NSData *sig = [pair signData: data];
   333     Log(@"Signature = %@ (%u bytes)",sig,sig.length);
   334     CAssert(sig);
   335     CAssert( [publicKey verifySignature: sig ofData: data] );
   336     
   337     // Now let's encrypt...
   338     NSData *crypted = [publicKey rawEncryptData: data];
   339     Log(@"Encrypted = %@ (%u bytes)",crypted,crypted.length);
   340     CAssert(crypted);
   341     CAssertEqual([pair rawDecryptData: crypted], data);
   342     Log(@"Verified decryption.");
   343     
   344     // Test creating a standalone public key:
   345     MYPublicKey *pub = [[MYPublicKey alloc] initWithKeyRef: publicKey.keyRef];
   346     CAssert( [pub verifySignature: sig ofData: data] );
   347     Log(@"Verified signature.");
   348     
   349     // Test creating a public key from data:
   350     Log(@"Reconstituting public key from data...");
   351     pub = [[MYPublicKey alloc] initWithKeyData: pubKeyData];
   352     CAssert(pub);
   353     CAssertEqual(pub.keyData, pubKeyData);
   354     CAssertEqual(pub.publicKeyDigest, pubKeyDigest);
   355     CAssert( [pub verifySignature: sig ofData: data] );
   356     [pub release];
   357     Log(@"Verified signature from reconstituted key.");
   358     
   359     // Test creating a public key from modulus+exponent:
   360     Log(@"Reconstituting public key from modulus+exponent...");
   361     pub = [[MYPublicKey alloc] initWithModulus: modulus exponent: exponent];
   362     CAssertEqual(pub.keyData, pubKeyData);
   363     [pub release];
   364 }
   365 
   366 
   367 static void TestWrapSessionKey( MYPrivateKey *privateKey ) {
   368 #if !TARGET_OS_IPHONE
   369     MYSymmetricKey *sessionKey = [MYSymmetricKey generateSymmetricKeyOfSize: 128 algorithm:kCCAlgorithmAES128];
   370     CAssert(sessionKey);
   371     NSData *cleartext = [@"This is a test. This is only a test." dataUsingEncoding: NSUTF8StringEncoding];
   372     NSData *encrypted = [sessionKey encryptData: cleartext];
   373 
   374     Log(@"Wrapping session key %@, %@", sessionKey, sessionKey.keyData);
   375     NSData *wrapped = [privateKey.publicKey wrapSessionKey: sessionKey];
   376     Log(@"Wrapped session key = %u bytes: %@", wrapped.length,wrapped);
   377     CAssert(wrapped.length >= 128/8);
   378     
   379     MYSymmetricKey *unwrappedKey = [privateKey unwrapSessionKey: wrapped
   380                                                   withAlgorithm: kCCAlgorithmAES128
   381                                                      sizeInBits: 128];
   382     Log(@"Unwrapped session key = %@, %@", unwrappedKey, unwrappedKey.keyData);
   383     CAssert(unwrappedKey);
   384     CAssertEq(unwrappedKey.algorithm, sessionKey.algorithm);
   385     CAssertEq(unwrappedKey.keySizeInBits, sessionKey.keySizeInBits);
   386     CAssertEqual(unwrappedKey.keyData, sessionKey.keyData);
   387 
   388     Log(@"Verifying that unwrapped key works");
   389     NSData *decrypted = [unwrappedKey decryptData: encrypted];
   390     CAssertEqual(decrypted, cleartext);
   391 #endif
   392 }
   393 
   394 
   395 TestCase(MYGenerateKeyPair) {
   396     RequireTestCase(MYKeychain);
   397     
   398     Log(@"Generating key pair...");
   399     MYPrivateKey *pair = [[MYKeychain defaultKeychain] generateRSAKeyPairOfSize: kTestCaseRSAKeySize];
   400     MYPublicKey *publicKey = pair.publicKey;
   401     Log(@"...created { %@ , %@ }.", pair, publicKey);
   402     
   403     @try{
   404         TestUseKeyPair(pair);
   405         TestWrapSessionKey(pair);
   406         
   407         [pair setName: @"Test KeyPair Label"];
   408         CAssertEqual(pair.name, @"Test KeyPair Label");
   409         CAssertEqual(publicKey.name, @"Test KeyPair Label");
   410 #if !TARGET_OS_IPHONE
   411         [pair setComment: @"This key-pair was generated automatically by a test case."];
   412         CAssertEqual(pair.comment, @"This key-pair was generated automatically by a test case.");
   413         CAssertEqual(publicKey.comment, @"This key-pair was generated automatically by a test case.");
   414 #endif
   415         [pair setAlias: @"TestCase@mooseyard.com"];
   416         CAssertEqual(pair.alias, @"TestCase@mooseyard.com");
   417         CAssertEqual(publicKey.alias, @"TestCase@mooseyard.com");
   418         
   419         CAssert([pair removeFromKeychain]);
   420         Log(@"Removed key-pair.");
   421         pair = nil;
   422         
   423     }@finally {
   424         if (pair) {
   425             if ([pair removeFromKeychain])
   426                 Log(@"Removed key-pair from keychain.");
   427             else
   428                 Warn(@"Unable to remove test key-pair from keychain");
   429         }
   430     }
   431 }
   432 
   433 
   434 #if !TARGET_OS_IPHONE
   435 TestCase(MYUseIdentity) {
   436     MYIdentity *me = nil;//[MYIdentity preferredIdentityForName: @"MYCryptoTest"];
   437     if (!me) {
   438         NSArray *idents = [[[MYKeychain allKeychains] enumerateIdentities] allObjects];
   439         SFChooseIdentityPanel *panel = [SFChooseIdentityPanel sharedChooseIdentityPanel];
   440         [panel setAlternateButtonTitle: @"Cancel"];
   441         if ([panel my_runModalForIdentities: idents 
   442                                     message: @"Choose an identity for the MYEncoder test case:"]
   443             != NSOKButton) {
   444             [NSException raise: NSGenericException format: @"User canceled"];
   445         }
   446         me = [panel my_identity];
   447         [me makePreferredIdentityForName: @"MYCryptoTest"];
   448     }
   449     CAssert(me,@"No default identity has been set up in the Keychain");
   450     TestUseKeyPair(me.privateKey);
   451 }
   452 #endif
   453 
   454 
   455 #pragma mark -
   456 #pragma mark KEYPAIR EXPORT:
   457 
   458 
   459 static void testKeyPairExportWithPrompt(BOOL withPrompt) {
   460     MYKeychain *keychain = [MYKeychain allKeychains];
   461     Log(@"Generating key pair...");
   462     MYPrivateKey *pair = [keychain generateRSAKeyPairOfSize: kTestCaseRSAKeySize];
   463     CAssert(pair);
   464     CAssert(pair.keyRef);
   465     CAssert(pair.publicKey.keyRef);
   466     Log(@"...created pair.");
   467     
   468     @try{
   469         NSData *pubKeyData = pair.publicKey.keyData;
   470         CAssert(pubKeyData.length >= kTestCaseRSAKeySize/8);
   471         [pair setName: @"Test KeyPair Label"];
   472         CAssertEqual(pair.name, @"Test KeyPair Label");
   473 #if !TARGET_OS_IPHONE
   474         [pair setComment: @"This key-pair was generated automatically by a test case."];
   475         CAssertEqual(pair.comment, @"This key-pair was generated automatically by a test case.");
   476 #endif
   477         [pair setAlias: @"TestCase@mooseyard.com"];
   478         CAssertEqual(pair.alias, @"TestCase@mooseyard.com");
   479         
   480 #if !TARGET_OS_IPHONE
   481         Log(@"Exporting key-pair...");
   482         NSString *passphrase = @"passphrase";
   483         NSData *privKeyData;
   484         if (withPrompt)
   485             privKeyData = [pair exportKey];
   486         else
   487             privKeyData = [pair _exportKeyInFormat: kSecFormatWrappedOpenSSL
   488                                           withPEM: YES
   489                                        passphrase: passphrase];
   490         Log(@"Exported data = %@ (%u bytes)", privKeyData,privKeyData.length);
   491         CAssert(privKeyData);
   492         [privKeyData writeToFile: @"ExportedPrivKey" atomically: YES];
   493 #endif
   494         
   495         // Check key lookup:
   496         Log(@"Looking up public key of pair in keychain...");
   497         MYSHA1Digest *digest = pair.publicKeyDigest;
   498         MYPublicKey *foundKey = [keychain publicKeyWithDigest: digest];
   499         CAssertEqual(foundKey, pair.publicKey);
   500         CAssert([keychain.enumeratePublicKeys.allObjects containsObject: pair.publicKey]);
   501         MYPrivateKey *foundPair = [keychain privateKeyWithDigest: digest];
   502         CAssertEqual(foundPair, pair);
   503         CAssert([keychain.enumeratePrivateKeys.allObjects containsObject: pair]);
   504         
   505         Log(@"Removing key-pair from keychain...");
   506         CAssert([pair removeFromKeychain]);
   507         pair = nil;
   508         CAssert([keychain publicKeyWithDigest: digest] == nil);
   509         
   510 #if !TARGET_OS_IPHONE
   511         Log(@"Importing key-pair...");
   512         if (withPrompt) {
   513             pair = [keychain importPublicKey: pubKeyData 
   514                                   privateKey: privKeyData];
   515         } else {
   516             pair = [[[MYPrivateKey alloc] _initWithKeyData: privKeyData
   517                                              publicKeyData: pubKeyData
   518                                                forKeychain: keychain.keychainRefOrDefault
   519                                                 passphrase: passphrase]
   520                     autorelease];
   521         }
   522         CAssert(pair);
   523         CAssertEqual(pair.publicKey.keyData, pubKeyData);
   524 #endif
   525     }@finally {
   526         if (pair) {
   527             if ([pair removeFromKeychain])
   528                 Log(@"Removed key-pair from keychain.");
   529             else
   530                 Warn(@"Unable to remove test key-pair from keychain");
   531         }
   532     }
   533 }
   534 
   535 TestCase(KeyPairExport) {
   536     RequireTestCase(MYKeychain);
   537     RequireTestCase(MYGenerateKeyPair);
   538     testKeyPairExportWithPrompt(NO);
   539 }
   540 
   541 TestCase(KeyPairExportWithUI) {
   542     RequireTestCase(KeyPairExport);
   543     testKeyPairExportWithPrompt(YES);
   544 }
   545 
   546 
   547 #endif DEBUG
   548