Certificate, Key, and Trust Services Tasks for iOS

This chapter describes and illustrates the use of Certificate, Key, and Trust Services functions to import an identity, evaluate the trust of a certificate, determine the cause of a trust failure, and recover from a trust failure.

The sequence of operations illustrated in this chapter is:

  1. Import an identity.

  2. Obtain a certificate from the imported data.

  3. Obtain a policy object for the policy used in evaluation of the certificate.

  4. Validate the certificate and evaluate whether it can be trusted as specified by the policy.

  5. Test for a recoverable trust error.

  6. Determine whether the trust error is due to an expired certificate.

  7. Change the evaluation criteria to ignore expired certificates.

  8. Reevaluate the certificate.

“Certificate, Key, and Trust Services Concepts” provides an introduction to the concepts and terminology of Certificate, Key, and Trust Services. For detailed information about all Certificate, Key, and Trust Services functions, see Certificate, Key, and Trust Services Reference.

The code snippets in this chapter assume that you have already included the headers listed below and that your software is already linked against the Core Foundation, Foundation, and Security frameworks.

#import <UIKit/UIKit.h>
#import <Security/Security.h>
#import <CoreFoundation/CoreFoundation.h>

Extracting and Evaluating an Identity From a *.P12 File

If you need a cryptographic identity (that is, a private key and its associated certificate) on an iOS-based device—for client-side authentication, for example—you can transfer it to the device securely as PKCS #12 data in a password-protected *.p12 file. This section shows how to extract the identity and trust objects from the PKCS #12 data and how to evaluate the trust.

Listing 2-1 shows sample code for using the SecPKCS12Import function to extract identity and trust objects from a *.p12 file and how to evaluate the trust. Listing 2-2 shows how to get the certificate from the identity and display certificate information. Explanations for numbered lines of code follow each listing.

Be sure to add the Security framework to your Xcode project when compiling code with this snippet.

Listing 2-1  Extracting identity and trust objects from PKCS #12 Data

OSStatus extractIdentityAndTrust(CFDataRef inPKCS12Data,
                                 SecIdentityRef *outIdentity,
                                 SecTrustRef *outTrust,
                                 CFStringRef keyPassword)
{
    OSStatus securityError = errSecSuccess;
 
 
    const void *keys[] =   { kSecImportExportPassphrase };
    const void *values[] = { keyPassword };
    CFDictionaryRef optionsDictionary = NULL;
 
    /* Create a dictionary containing the passphrase if one
       was specified.  Otherwise, create an empty dictionary. */
    optionsDictionary = CFDictionaryCreate(
                                                  NULL, keys,
                                                  values, (keyPassword ? 1 : 0),
                                                  NULL, NULL);  // 1
 
    CFArrayRef items = NULL;
    securityError = SecPKCS12Import(inPKCS12Data,
                                    optionsDictionary,
                                    &items);                    // 2
 
 
    //
    if (securityError == 0) {                                   // 3
        CFDictionaryRef myIdentityAndTrust = CFArrayGetValueAtIndex (items, 0);
        const void *tempIdentity = NULL;
        tempIdentity = CFDictionaryGetValue (myIdentityAndTrust,
                                                       kSecImportItemIdentity);
        CFRetain(tempIdentity);
        *outIdentity = (SecIdentityRef)tempIdentity;
        const void *tempTrust = NULL;
        tempTrust = CFDictionaryGetValue (myIdentityAndTrust, kSecImportItemTrust);
 
        CFRetain(tempTrust);
        *outTrust = (SecTrustRef)tempTrust;
    }
 
    if (optionsDictionary)                                      // 4
        CFRelease(optionsDictionary);
 
    if (items)
        CFRelease(items);
 
    return securityError;
}

This code assumes that:

Here’s what the code does:

  1. Sets up a dictionary containing the password to pass to SecPKCS12Import. Notice that core foundation dictionaries—as used here—and the NSDictionary class are entirely equivalent. See Listing 2-9 for an example using NSDictionary methods.

  2. Extracts the certificate, key, and trust from the PKCS #12 data and puts them in an array.

  3. Gets the first dictionary out of the array and gets the identity and trust out of the dictionary. The SecPKCS12Import function returns one dictionary for each item (identity or certificate) in the PKCS #12 data. In this sample, the identity being extracted is the first one in the array (item #0).

  4. Disposes of the options dictionary and items array, which are no longer needed.

After performing these steps, you should typically:

  1. Release the CFDataRef object that contains the raw data.

  2. Evaluate the trust by calling SecTrustEvaluate or SecTrustEvaluateAsync on the returned trust object.

  3. Handle the trust result.

    If the trust result is kSecTrustResultInvalid, kSecTrustResultDeny, kSecTrustResultFatalTrustFailure, you cannot proceed and should fail gracefully.

    If the trust result is kSecTrustResultRecoverableTrustFailure, you might be able to recover from the failure. See “Recovering From a Trust Failure.”

The following listing shows how to get the certificate from the identity and how to display information from the certificate. Be sure to add the Security framework to your Xcode project when compiling code with this snippet.

Listing 2-2  Displaying information from the certificate

NSString *copySummaryString(SecIdentityRef identity)
{
    // Get the certificate from the identity.
    SecCertificateRef myReturnedCertificate = NULL;
    OSStatus status = SecIdentityCopyCertificate (identity,
                                      &myReturnedCertificate);  // 1
 
    if (status) {
        NSLog(@"SecIdentityCopyCertificate failed.\n");
        return NULL;
    }
 
    CFStringRef certSummary = SecCertificateCopySubjectSummary
                                      (myReturnedCertificate);  // 2
 
    NSString* summaryString = [[NSString alloc]
                       initWithString:(__bridge NSString *)certSummary];  // 3
 
    CFRelease(certSummary);
 
    return summaryString;
}

Here’s what the code does:

  1. Extracts the certificate from the identity.

  2. Gets summary information from the certificate.

  3. Converts the string to an NSString object so it can be displayed.

  4. Releases the NSString object.

Getting and Using Persistent Keychain References

When you add an item to the keychain or find an item in the keychain, you can request a persistent reference. Because a persistent reference remains valid between invocations of your program and can be stored on disk, you can use one to make it easier to find a keychain item that you will need repeatedly. The following code sample shows how to obtain a persistent reference for the identity object obtained in Listing 2-1.

Listing 2-3  Getting a persistent reference for an identity

CFDataRef persistentRefForIdentity(SecIdentityRef identity)
{
    OSStatus status = errSecSuccess;
 
    CFTypeRef  persistent_ref = NULL;
    const void *keys[] =   { kSecReturnPersistentRef, kSecValueRef };
    const void *values[] = { kCFBooleanTrue,          identity };
    CFDictionaryRef dict = CFDictionaryCreate(NULL, keys, values,
                                              2, NULL, NULL);
    status = SecItemAdd(dict, &persistent_ref);
 
    if (dict)
        CFRelease(dict);
 
    return (CFDataRef)persistent_ref;
}

The following sample shows how to retrieve the identity object from the keychain using the persistent reference.

Listing 2-4  Getting an identity using a persistent reference

SecIdentityRef identityForPersistentRef(CFDataRef persistent_ref)
{
    CFTypeRef   identity_ref     = NULL;
    const void *keys[] =   { kSecClass, kSecReturnRef,  kSecValuePersistentRef };
    const void *values[] = { kSecClassIdentity, kCFBooleanTrue, persistent_ref };
    CFDictionaryRef dict = CFDictionaryCreate(NULL, keys, values,
                                              3, NULL, NULL);
    SecItemCopyMatching(dict, &identity_ref);
 
    if (dict)
        CFRelease(dict);
 
    return (SecIdentityRef)identity_ref;
}

Finding a Certificate In the Keychain

The following code sample shows how to find a certificate in the keychain using the name of the certificate to identify it. To find a keychain item using a persistent reference, see Listing 2-4. To find a keychain item using an identifier string stored as a keychain item attribute, see “Encrypting and Decrypting Data.” An explanation for each numbered line of code follows the listing.

Listing 2-5  Finding a certificate In the Keychain

    OSStatus status = errSecSuccess;
    CFTypeRef   certificateRef     = NULL;                      // 1
    const char *certLabelString = "Romeo Montague";
    CFStringRef certLabel = CFStringCreateWithCString(
                                NULL, certLabelString,
                                kCFStringEncodingUTF8);         // 2
 
    const void *keys[] =   { kSecClass, kSecAttrLabel, kSecReturnRef };
    const void *values[] = { kSecClassCertificate, certLabel, kCFBooleanTrue };
    CFDictionaryRef dict = CFDictionaryCreate(NULL, keys,
                                               values, 3,
                                             NULL, NULL);       // 3
    status = SecItemCopyMatching(dict, &certificateRef);        // 4
 
    if (status == errSecSuccess) {
        CFRelease(certificateRef);
        certificateRef = NULL;
    }
 
    /* Do something with certificateRef here */
 
    if (dict)
        CFRelease(dict);

Here’s what the code does:

  1. Defines a variable to hold the certificate object.

  2. Creates a string containing the name of the certificate.

  3. Creates a dictionary of attributes to be used in the certificate search. The kSecReturnRef key specifies that the function should return a reference to the keychain item when it’s found.

  4. Searches for the certificate in the keychain.

Obtaining a Policy Object and Evaluating Trust

Before you can evaluate the trust of a certificate, you must obtain a reference object for the certificate. You can obtain a certificate object by extracting it from an identity (see Listing 2-2), by creating one from DER certificate data using the SecCertificateCreateWithData function (see the following sample: Listing 2-6), or by finding the certificate on a keychain (Listing 2-5).

The criteria for evaluation of trust are set by trust policies. Listing 2-6 shows how you can obtain a policy object for use in an evaluation. There are two policies available in iOS for this purpose: Basic X509 and SSL (see “AppleX509TP Trust Policies”). You use the SecPolicyCreateBasicX509 or SecPolicyCreateSSL function to obtain the policy object.

The following code sample shows how to obtain a policy object and use it to evaluate trust of a certificate. An explanation for each numbered line of code follows the listing.

Listing 2-6  Obtaining a policy reference object and evaluating trust

    NSString *thePath = [[NSBundle mainBundle]
                          pathForResource:@"Romeo Montague" ofType:@"cer"];
    NSData *certData = [[NSData alloc]
                         initWithContentsOfFile:thePath];
    CFDataRef myCertData = (__bridge CFDataRef)certData;                 // 1
 
    SecCertificateRef myCert;
    myCert = SecCertificateCreateWithData(NULL, myCertData);    // 2
 
    SecPolicyRef myPolicy = SecPolicyCreateBasicX509();         // 3
 
    SecCertificateRef certArray[1] = { myCert };
    CFArrayRef myCerts = CFArrayCreate(
                                       NULL, (void *)certArray,
                                       1, NULL);
    SecTrustRef myTrust;
    OSStatus status = SecTrustCreateWithCertificates(
                                                    myCerts,
                                                    myPolicy,
                                                    &myTrust);  // 4
 
    SecTrustResultType trustResult;
    if (status == noErr) {
        status = SecTrustEvaluate(myTrust, &trustResult);       // 5
    }
//...                                                             // 6
    if (trustResult == kSecTrustResultRecoverableTrustFailure) {
        // ...;
    }
// ...
    if (myPolicy)
        CFRelease(myPolicy);                                    // 7

Here’s what the code does:

  1. Finds the certificate file and gets the data. In this example, the file is included in the application bundle. However, you can transfer the certificate to your application over a network if you prefer. If the certificate is already in the keychain, see “Finding a Certificate In the Keychain.”

  2. Creates a certificate reference from the certificate data.

  3. Creates a policy to be used in evaluating trust.

  4. Creates a trust object using the certificate and the policy. If you have intermediate certificates or an anchor certificate for the certificate chain, you can include those in the certificate array passed to the SecTrustCreateWithCertificates function. Doing so speeds up the trust evaluation.

  5. Evaluates the trust.

  6. Handles the trust result. If the trust result is kSecTrustResultInvalid, kSecTrustResultDeny, kSecTrustResultFatalTrustFailure, you cannot proceed and should fail gracefully. If the trust result is kSecTrustResultRecoverableTrustFailure, you might be able to recover from the failure. See “Recovering From a Trust Failure.”

  7. Disposes of the policy object at the end of the routine, after it has been used to evaluate the trust.

Recovering From a Trust Failure

There are several possible results of a trust evaluation, depending on such factors as whether all the certificates in the chain were found, whether they are all valid, and what the user trust settings are for the certificates. It is up to your application to determine the course of action based on the result of the evaluation. For example, if the result is kSecTrustResultConfirm, you should display a dialog requesting that the user give permission to proceed.

The evaluation result kSecTrustResultRecoverableTrustFailure indicates that trust was denied, but that it is possible to change settings to get a different result. For example, if the certificate used to sign a document has expired, you can change the date used for the evaluation to see whether the certificate was valid when the document was signed. The code in Listing 2-7 illustrates how to change the evaluation date. Note that the CFDateCreate function takes an absolute time (the number of seconds since 1 January 2001); you can use the CFGregorianDateGetAbsoluteTime function to convert a calendar date and time into an absolute time. An explanation for each numbered line of code follows the listing.

Listing 2-7  Setting an evaluation date

void recoverFromTrustFailure(SecTrustRef myTrust)
{
    SecTrustResultType trustResult;
    OSStatus status = SecTrustEvaluate(myTrust, &trustResult);  // 1
 
    //Get time used to verify trust
    CFAbsoluteTime trustTime,currentTime,timeIncrement,newTime;
    CFDateRef newDate;
    if (trustResult == kSecTrustResultRecoverableTrustFailure) {// 2
        trustTime = SecTrustGetVerifyTime(myTrust);             // 3
        timeIncrement = 31536000;                               // 4
        currentTime = CFAbsoluteTimeGetCurrent();               // 5
        newTime = currentTime - timeIncrement;                  // 6
        if (trustTime - newTime){                               // 7
            newDate = CFDateCreate(NULL, newTime);              // 8
            SecTrustSetVerifyDate(myTrust, newDate);            // 9
            status = SecTrustEvaluate(myTrust, &trustResult);   // 10
        }
    }
    if (trustResult != kSecTrustResultProceed) {                // 11
//...
    }
}

Here’s what the code does:

  1. Evaluates the trust of the certificate. See “Obtaining a Policy Object and Evaluating Trust.”

  2. Checks whether the result of the trust evaluation was a recoverable trust failure.

  3. Gets the absolute time that was used to evaluate the trust. If the certificate expired before this time, then it is considered invalid.

  4. Sets a time increment equal to the number of seconds in a year.

  5. Gets the current (absolute) time.

  6. Subtracts a year from the current time.

  7. Checks whether the time used to evaluate trust was more recent than one year before the current time. If it was, then the trust is evaluated again using the new time; that is, the certificate is checked to see if it failed verification because it expired sometime in the past year.

  8. Converts the new time to a CFDateRef. You can also use NSDate to manipulate the dates; CFDateRef and NSDate are toll-free bridged, meaning in a method where you see an NSDate * parameter, you can pass in a CFDateRef, and in a function where you see a CFDateRef parameter, you can pass in an instance of NSDate or of a concrete subclass of NSDate.

  9. Sets the date used to verify trust to the new time (a year earlier).

  10. Reevaluates the trust. If the reason the trust evaluation failed was because the certificate expired within a year of the current time, the evaluation should now succeed.

  11. Checks whether the evaluation now succeeds. If not, you can try something else, such as asking the user to install an intermediate certificate, or you can tell the user that the certificate is not valid and fail gracefully.

Encrypting and Decrypting Data

The Certificate, Key, and Trust API includes functions for generating asymmetric key pairs and using them to encrypt and decrypt data. You might want to use this feature to encrypt data that you do not want to be accessible in backup data, for example. Or, you can use a private-public key pair shared between your iOS application and a desktop application to send encrypted data over a network. The code in Listing 2-8 shows how to generate a public-private key pair for use on the mobile device. Listing 2-9 shows how to use a public key to encrypt data using Certificate, Key, and Trust functions, and Listing 2-10 shows how to use a private key to decrypt data. Notice that these samples use Cocoa objects (such as NSMutableDictionary) rather than the core foundation objects (such as CFMutableDictionaryRef) used in other samples in this chapter. The Cocoa objects and their Core Foundation counterparts are completely equivalent and are toll-free bridged; for example, in a method where you see an NSMutableDictionary * parameter, you can pass in a CFMutableDictionaryRef, and in a function where you see a CFMutableDictionaryRef parameter, you can pass in an instance of NSMutableDictionary. Explanations for numbered lines of code follow each listing.

Listing 2-8  Generating a key pair

static const UInt8 publicKeyIdentifier[] = "com.apple.sample.publickey\0";
static const UInt8 privateKeyIdentifier[] = "com.apple.sample.privatekey\0";
 
- (void)generateKeyPairPlease
{
    OSStatus status = noErr;
    NSMutableDictionary *privateKeyAttr = [[NSMutableDictionary alloc] init];
    NSMutableDictionary *publicKeyAttr = [[NSMutableDictionary alloc] init];
    NSMutableDictionary *keyPairAttr = [[NSMutableDictionary alloc] init];
                                                                // 2
 
    NSData * publicTag = [NSData dataWithBytes:publicKeyIdentifier
                                length:strlen((const char *)publicKeyIdentifier)];
    NSData * privateTag = [NSData dataWithBytes:privateKeyIdentifier
                               length:strlen((const char *)privateKeyIdentifier)];
                                                                // 3
 
    SecKeyRef publicKey = NULL;
    SecKeyRef privateKey = NULL;                                // 4
 
    [keyPairAttr setObject:(__bridge id)kSecAttrKeyTypeRSA
                                   forKey:(__bridge id)kSecAttrKeyType]; // 5
    [keyPairAttr setObject:[NSNumber numberWithInt:1024]
                             forKey:(__bridge id)kSecAttrKeySizeInBits]; // 6
 
    [privateKeyAttr setObject:[NSNumber numberWithBool:YES]
                               forKey:(__bridge id)kSecAttrIsPermanent]; // 7
    [privateKeyAttr setObject:privateTag
                            forKey:(__bridge id)kSecAttrApplicationTag]; // 8
 
    [publicKeyAttr setObject:[NSNumber numberWithBool:YES]
                               forKey:(__bridge id)kSecAttrIsPermanent]; // 9
    [publicKeyAttr setObject:publicTag
                            forKey:(__bridge id)kSecAttrApplicationTag]; // 10
 
    [keyPairAttr setObject:privateKeyAttr
                               forKey:(__bridge id)kSecPrivateKeyAttrs]; // 11
    [keyPairAttr setObject:publicKeyAttr
                                forKey:(__bridge id)kSecPublicKeyAttrs]; // 12
 
    status = SecKeyGeneratePair((__bridge CFDictionaryRef)keyPairAttr,
                                      &publicKey, &privateKey); // 13
//    error handling...
 
 
    if(publicKey) CFRelease(publicKey);
    if(privateKey) CFRelease(privateKey);                       // 14
}

Here’s what the code does:

  1. Defines unique strings to be added as attributes to the private and public key keychain items to make them easier to find later.

  2. Allocates dictionaries to be used for attributes in the SecKeyGeneratePair function.

  3. Creates NSData objects that contain the identifier strings defined in step 1.

  4. Allocates SecKeyRef objects for the public and private keys.

  5. Sets the key-type attribute for the key pair to RSA.

  6. Sets the key-size attribute for the key pair to 1024 bits.

  7. Sets an attribute specifying that the private key is to be stored permanently (that is, put into the keychain).

  8. Adds the identifier string defined in steps 1 and 3 to the dictionary for the private key.

  9. Sets an attribute specifying that the public key is to be stored permanently (that is, put into the keychain).

  10. Adds the identifier string defined in steps 1 and 3 to the dictionary for the public key.

  11. Adds the dictionary of private key attributes to the key-pair dictionary.

  12. Adds the dictionary of public key attributes to the key-pair dictionary.

  13. Generates the key pair.

  14. Releases memory that is no longer needed.

You can send your public key to anyone, who can then use it to encrypt data. Assuming you keep your private key secure, then only you will be able to decrypt the data. The following code sample shows how to encrypt data using a public key. This can be a public key that you generated on the device (see the preceding code sample) or a public key that you extracted from a certificate that was sent to you or that is in your keychain. You can use the SecTrustCopyPublicKey function to extract a public key from a certificate. In the following code sample, the key is assumed to have been generated on the device and placed in the keychain. An explanation for each numbered line of code follows the listing.

Listing 2-9  Encrypting data with a public key

- (NSData *)encryptWithPublicKey
{
    OSStatus status = noErr;
 
    size_t cipherBufferSize;
    uint8_t *cipherBuffer;                     // 1
 
// [cipherBufferSize]
    const uint8_t dataToEncrypt[] = "the quick brown fox jumps "
                            "over the lazy dog\0"; // 2
    size_t dataLength = sizeof(dataToEncrypt)/sizeof(dataToEncrypt[0]);
 
    SecKeyRef publicKey = NULL;                                 // 3
 
    NSData * publicTag = [NSData dataWithBytes:publicKeyIdentifier
             length:strlen((const char *)publicKeyIdentifier)]; // 4
 
    NSMutableDictionary *queryPublicKey =
                            [[NSMutableDictionary alloc] init]; // 5
 
    [queryPublicKey setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
    [queryPublicKey setObject:publicTag forKey:(__bridge id)kSecAttrApplicationTag];
    [queryPublicKey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
    [queryPublicKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnRef];
                                                                // 6
 
    status = SecItemCopyMatching
    ((__bridge CFDictionaryRef)queryPublicKey, (CFTypeRef *)&publicKey); // 7
 
//  Allocate a buffer
 
    cipherBufferSize = SecKeyGetBlockSize(publicKey);
    cipherBuffer = malloc(cipherBufferSize);
 
//  Error handling
 
    if (cipherBufferSize < sizeof(dataToEncrypt)) {
        // Ordinarily, you would split the data up into blocks
        // equal to cipherBufferSize, with the last block being
        // shorter. For simplicity, this example assumes that
        // the data is short enough to fit.
        printf("Could not decrypt.  Packet too large.\n");
        return NULL;
    }
 
    // Encrypt using the public.
    status = SecKeyEncrypt(    publicKey,
                                kSecPaddingPKCS1,
                                dataToEncrypt,
                                (size_t) dataLength,
                                cipherBuffer,
                                &cipherBufferSize
                                );                              // 8
 
//  Error handling
//  Store or transmit the encrypted text
 
    if (publicKey) CFRelease(publicKey);
 
    NSData *encryptedData = [NSData dataWithBytes:cipherBuffer length:dataLength];
 
    free(cipherBuffer);
 
    return encryptedData;
}

Here’s what the code does:

  1. Allocates a buffer to hold the encrypted text.

  2. Specifies the text to be encrypted.

  3. Allocates a SecKeyRef object for the public key.

  4. Creates an NSData object containing the unique string used to identify the public key in the keychain (see steps 1, 3, and 8 in Listing 2-8).

  5. Allocates the dictionary to be used to find the public key in the keychain.

  6. Specifies the key-value attribute pairs for the dictionary to be used to find the public key in the keychain. The attributes specify that the keychain item is an encryption key; that the keychain item has an attribute containing the unique string specified in step 4; that the item is an RSA key; and that a reference to the keychain item is to be returned.

  7. Calls the SecItemCopyMatching function to find the key in the keychain.

  8. Encrypts the data from step 2 using the key returned by the SecItemCopyMatching function in step 7 using PKCS1 padding.

  9. Releases memory that is no longer needed.

The following code sample shows how to decrypt data. This sample uses the private key corresponding to the public key used to encrypt the data, and assumes you already have the cipher text created in the preceding example. It gets the private key from the keychain using the same technique as used in the preceding example to get the public key.

Listing 2-10  Decrypting with a private key

- (void)decryptWithPrivateKey: (NSData *)dataToDecrypt
{
    OSStatus status = noErr;
 
    size_t cipherBufferSize = [dataToDecrypt length];
    uint8_t *cipherBuffer = (uint8_t *)[dataToDecrypt bytes];
 
    size_t plainBufferSize;
    uint8_t *plainBuffer;
 
    SecKeyRef privateKey = NULL;
 
    NSData * privateTag = [NSData dataWithBytes:privateKeyIdentifier
                            length:strlen((const char *)privateKeyIdentifier)];
 
    NSMutableDictionary *queryPrivateKey = [[NSMutableDictionary alloc] init];
 
    // Set the private key query dictionary.
    [queryPrivateKey setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
    [queryPrivateKey setObject:privateTag forKey:(__bridge id)kSecAttrApplicationTag];
    [queryPrivateKey setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
    [queryPrivateKey setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnRef];
                                                                // 1
 
    status = SecItemCopyMatching
  ((__bridge CFDictionaryRef)queryPrivateKey, (CFTypeRef *)&privateKey); // 2
 
    //  Allocate the buffer
    plainBufferSize = SecKeyGetBlockSize(privateKey);
    plainBuffer = malloc(plainBufferSize);
 
    if (plainBufferSize < cipherBufferSize) {
        // Ordinarily, you would split the data up into blocks
        // equal to plainBufferSize, with the last block being
        // shorter. For simplicity, this example assumes that
        // the data is short enough to fit.
        printf("Could not decrypt.  Packet too large.\n");
        return;
    }
 
//  Error handling
 
    status = SecKeyDecrypt(    privateKey,
                                kSecPaddingPKCS1,
                                cipherBuffer,
                                cipherBufferSize,
                                plainBuffer,
                                &plainBufferSize
                                );                              // 3
 
//  Error handling
//  Store or display the decrypted text
 
    if(privateKey) CFRelease(privateKey);
}

Here’s what the code does:

  1. Sets up the dictionary used to find the private key in the keychain.

  2. Finds the private key in the keychain.

  3. Decrypts the data.

  4. Releases memory that is no longer needed.