This post is the second in a series of articles focused on Cryptography in iOS, concretely, in Swift. In the first post of the series, we explored Symmetric Cryptography. In this post, we are going to learn about Asymmetric Cryptography in Swift.

Furthermore, I will also provide you with a class called AsymmetricCryptoManager, a ready-to-use-class to easily perform asymmetric cryptography operations without any hassle.

You can access all the code for the AsymmetricCryptoManager from my Github repository.

About Asymmetric Cryptography

Asymmetric Cryptography, also called public-key cryptography, uses a pair of different but mathematically related keys for ciphering and deciphering.

One of them is called the “private key”, and is supposed to be kept secure and private. The other one is the public key, and it can be safely shared and distributed.

The awesome concept behind this scheme is that you can only decipher a text ciphered with the public key by using the private key and vice-versa. Thus, you can encrypt a message for a user with her public key, and only she will be able to decrypt it by using her private key.

500px-Public_key_encryption.svg

Another well-known use for Asymmetric Cryptography is verifying the identity of a user by means of a digital signature.

First, the user generates a hash of some data and “signs” it with his private key, generating a signature. Then, the user passes the data and the signature to another user.

This other user can verify the signature using the public key of the sender, which is publicly available, thus, verifying that the message actually originated from the sender and was not tainted during the communication process.

For further information on the subject, you can have a look at the Wikipedia page on public-key cryptography and digital signature.

The problem with The Security Framework, CommonCrypto, and Swift.

Some years ago, OS X cryptography was performed by accessing to the OpenSSL library. However, Apple deprecated it in favor of the Security framework and the set of CommonCrypto libraries, that became the standard for cryptographic operations in iOS and OS X. Unfortunately, The Security Framework and CommonCrypto consist of a bunch of poorly documented, C-style calls, that certainly are obscure and unfriendly for developers.

Furthermore, in swift, things get even worse because of its intentional departure from anything C-related. Besides, CommonCrypto is really picky with the parameters and options for its methods, and most of the times the error messages can be hard to understand.

Nevertheless, I will try to share my experience on how to avoid headaches when using CommonCrypto in Swift.

In order to use Asymmetric Cryptography functions in swift, first we need to add a bridging header to our project, and then import CommonCrypto:

#import <CommonCrypto/CommonCrypto.h>

Key management and the Keychain.

(Almost) all key management in the Security Framework happens at the keychain level. The keychain is a secure store that keeps all the keys, certificates and cryptographic objects belonging to the user of an application.

Thus, you create the keys and add them to the keychain. You search for them and recover them (in the form of references) from the keychain to use it for encryption or decryption. Finally, you delete them from the keychain when you no longer need them.

The keychain is shared among all applications of the user and has a dedicated App in OS X called Keychain Access. Two Apps can make use of the same keychain items if they are properly configured to do so.

However, for now, the only thing you need to know about the keychain is that you need it to store, retrieve and use your keys.

Creating our Asymmetric Cryptography Manager

Our manager will follow the Singleton design pattern. We will create a sharedInstance property for accessing our singleton:

private let _singletonInstance = AsymmetricCryptoManager()

class AsymmetricCryptoManager: NSObject {
   /** Shared instance */
   class var sharedInstance: AsymmetricCryptoManager {
      return _singletonInstance
   }
}

Generating our key pair.

The first thing we need to start operating within an Asymmetric Cryptography scheme is a pair of public-private keys.

In order to create them, we need to call SecKeyGeneratePair. This function needs a dictionary with the desired properties for our key pair. Usually, you need to specify some global parameters (like the type of key, its length, etc) and some specific properties for the public and private keys separately.

As an example, we will use 2048 bits RSA keys. Besides, we will set a tag to allow us to find the keys later thanks to the property kSecAttrApplicationTag.

private let kAsymmetricCryptoManagerApplicationTag = "com.AsymmetricCrypto.keypair"

private let kAsymmetricCryptoManagerKeyType = kSecAttrKeyTypeRSA private let kAsymmetricCryptoManagerKeySize = 2048 // private key parameters let privateKeyParams: [String: AnyObject] = [ kSecAttrIsPermanent as String: true, kSecAttrApplicationTag as String: kAsymmetricCryptoManagerApplicationTag ] // private key parameters let publicKeyParams: [String: AnyObject] = [ kSecAttrIsPermanent as String: true, kSecAttrApplicationTag as String: kAsymmetricCryptoManagerApplicationTag ] // global parameters for our key generation let parameters: [String: AnyObject] = [ kSecAttrKeyType as String: kSecAttrKeyTypeRSA, kSecAttrKeySizeInBits as String: 2048, kSecPublicKeyAttrs as String: publicKeyParams, kSecPrivateKeyAttrs as String: privateKeyParams, ]

As a result of setting kSecAttrIsPermanent to true on both the private and the public key dictionaries, we are specifying that we want our keys to be stored permanently in the keychain. The global parameters specify the type of the key to RSA (kSecAttrKeyTypeRSA) and its length to 2048 bits.

These property dictionaries will be passed then to SecKeyGeneratePair. Then, the results will be stored in two output parameters, the public key and the private key references, both instances of SecKeyRef.

As Asymmetric Cryptography operations can be time-consuming, we don’t want to perform them in the main queue. Thus, we will use a background thread.

func createSecureKeyPair(completion: ((success: Bool, error: AsymmetricCryptoException?) -> Void)? = nil) {
   // generate the parameters dictionary as we specified earlier
   // ...
 
   // asynchronously generate the key pair and call the completion block
   dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { () -> Void in
      var pubKey, privKey: SecKeyRef?
      let status = SecKeyGeneratePair(parameters, &pubKey, &privKey)
 
      if status == errSecSuccess {
         dispatch_async(dispatch_get_main_queue(), { completion?(success: true, error: nil) })
      } else {
         var error = AsymmetricCryptoException.UnknownError
         switch (status) {
            case errSecDuplicateItem: error = .DuplicateFoundWhileTryingToCreateKey
            case errSecItemNotFound: error = .KeyNotFound
            case errSecAuthFailed: error = .AuthFailed
            default: break
         }
         dispatch_async(dispatch_get_main_queue(), { completion?(success: false, error: error) })
       }
    }
 }

Here, an error is an Enumeration called AsymmetricCryptoException, implementing ErrorType. It describes the errors that can be thrown when performing cryptographic operations.

The SecKeyGeneratePair returns a status code, hopefully, errSecSuccess if the operation was successful.

Retrieving our keys from the Keychain.

In order to use our keys, we need to retrieve them from the keychain. We do that by means of a search, specifying a set of properties for the key we are looking for, and then calling SecItemCopyMatching.

The properties to look for are specified in a dictionary. They must be specific enough to retrieve the key we are looking for. In this dictionary, we can specify whether we want to retrieve a reference (kSecReturnRef set to true) or whether we want the data for the key (kSecReturnData set to true).

Note that you cannot always obtain the data for a key (for instance, you cannot retrieve the data of a private key stored in the Secure Enclave), but for now let’s assume that we want just the reference, so we will define our getPublicKeyReference() method as follow:

private func getPublicKeyReference() -> SecKeyRef? {
   let parameters = [
     kSecClass as String: kSecClassKey,
     kSecAttrKeyType as String: kSecAttrKeyTypeRSA,
     kSecAttrApplicationTag as String: kAsymmetricCryptoManagerApplicationTag,
     kSecAttrKeyClass as String: kSecAttrKeyClassPublic,
     kSecReturnRef as String: true,
   ]
   var ref: AnyObject?
   let status = SecItemCopyMatching(parameters, &ref)
   if status == errSecSuccess { return ref as! SecKeyRef? } 
   else { return nil }
}

Step By Step

First, we are specifying that we want a key (kSecClass = kSecClassKey), of type RSA (kSecAttrKeyType = kSecAttrKeyTypeRSA). Next we specify our application tag, and the public key (kSecAttrKeyClass = kSecAttrKeyClassPublic).

Finally, by setting kSecReturnRef to true, we indicate the Security framework that we expect the result to be a valid SecKeyRef.

If this method returns a status code of errSecItemNotFound (-25300), the key was not found, and we know that we need to generate our key pair. The OSStatus codes from Security framework are somewhat cryptic (easy joke!). However, I can recommend you this link to easily find out the meaning of a concrete code.

Conversely, if the code is errSecSuccess (0), we have a valid key reference, and we can start using it for encryption or decryption.

The method for retrieving the private key is exactly the same, except for the value of the parameter kSecAttrKeyClass being kSecAttrKeyClassPrivate.

Secure Messaging Scenario.

One of the most common uses for asymmetric cryptography is building a secure messaging system. The idea is allowing the users to send encrypted messages to another user, in a way that only this receiving user can decrypt and read the message.

We need two methods:

  • One to encrypt the message that will be sent to a user (using the public key of that user)
  • Another one to decrypt that message in the receiver’s end (with the corresponding private key).

Encrypting With The Public Key

We will implement a method in our Asymmetric Cryptography Manager that will do just that by means of the method SecKeyEncrypt.

As always, we will use a background thread to avoid blocking the application’s main thread.

func encryptMessageWithPublicKey(message: String, completion: (success: Bool, data: NSData?, error: AsymmetricCryptoException?) -> Void) {
   dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { () -> Void in
      if let publicKeyRef = self.getPublicKeyReference() {
         // prepare input input plain text
         guard let messageData = message.dataUsingEncoding(NSUTF8StringEncoding) 
         else {
           completion(success: false, data: nil, error: .WrongInputDataFormat)
           return
         }
         let plainText = UnsafePointer<UInt8>(messageData.bytes)
         let plainTextLen = messageData.length
 
         // prepare output data buffer
         guard let cipherData = NSMutableData(length: SecKeyGetBlockSize(publicKeyRef)) 
         else {
           completion(success: false, data: nil, error: .OutOfMemory)
           return
         }
         let cipherText = UnsafeMutablePointer<UInt8>(cipherData.mutableBytes)
         var cipherTextLen = cipherData.length
 
         let status = SecKeyEncrypt(publicKeyRef, .PKCS1, plainText, plainTextLen, cipherText, &cipherTextLen)
 
         // analyze results and call the completion in main thread
         dispatch_async(dispatch_get_main_queue(), { () -> Void in
            completion(success: status == errSecSuccess, data: cipherData, 
               error: status == errSecSuccess ? nil : .UnableToEncrypt)
            cipherText.destroy()
         })
         return
      } else { 
         dispatch_async(dispatch_get_main_queue(), { completion(success: false, data: nil, error: .KeyNotFound) }) 
      }
   }
}

How The Encryption Process Works

First, we retrieve the public key with the method getPublicKeyReference we just created. Then we will prepare the input buffer with our plain data and the output buffer to store the encrypted data. Being a C API, we need to make use of UnsafePointers for the constant input data, and UnsafeMutablePointers for the output buffers that will hold the result of the cryptographic operations.

The procedure is always the same:

  • For input (constant) data, first, create a let variable of type UnsafePointer<UInt8> (that’s your C-style const unsigned char * buffer). Then, initialize it with the bytes of the input NSData (data.bytes). Finally, use data.length as the associated length parameter.
  • For output data, first, create a let variable of type UnsafeMutablePointer<UInt8> (that’s your C-style unsigned char * buffer). Then, initialize it with the mutableBytes of the output NSData (result.mutableBytes). Finally, use result.length as the associated length parameter.

Once we have our input and output buffers, we are ready to call the function. The result status will allow us to check if the operation was successful. In that case, we can safely return our output data, linked with our output buffer by means of mutableBytes. Also, it’s always a good practice to free the reserved output buffer memory. You can do that by using the destroy method on our UnsafeMutablePointer<UInt8> variable.

Decrypting With Our Private Key

When the user receives a message encrypted with his public key, he must decrypt it with his private key. We will define a method that will make use of SecKeyDecrypt to recover the plain text:

func decryptMessageWithPrivateKey(encryptedData: NSData, completion: (success: Bool, result: String?, error: AsymmetricCryptoException?) -> Void) {
   dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { () -> Void in
 
      if let privateKeyRef = self.getPrivateKeyReference() {
         // prepare input input plain text
         let encryptedText = UnsafePointer<UInt8>(encryptedData.bytes)
         let encryptedTextLen = encryptedData.length
 
         // prepare output data buffer
         guard let plainData = NSMutableData(length: kAsymmetricCryptoManagerCypheredBufferSize) 
         else {
            completion(success: false, result: nil, error: .OutOfMemory)
            return
         }
         let plainText = UnsafeMutablePointer<UInt8>(plainData.mutableBytes)
         var plainTextLen = plainData.length
 
         let status = SecKeyDecrypt(privateKeyRef, .PKCS1, encryptedText, encryptedTextLen, plainText, &plainTextLen)

         // analyze results and call the completion in main thread
         dispatch_async(dispatch_get_main_queue(), { () -> Void in
            if status == errSecSuccess {
               // adjust NSData length
               plainData.length = plainTextLen
               // Generate and return result string
               if let string = NSString(data: plainData, encoding: NSUTF8StringEncoding) as? String {
                  completion(success: true, result: string, error: nil)
               } else { 
                  completion(success: false, result: nil, error: .UnableToDecrypt) 
               }
            } else { 
               completion(success: false, result: nil, error: .UnableToDecrypt) 
            }
            plainText.destroy()
         })
         return
      } else { 
         dispatch_async(dispatch_get_main_queue(), { completion(success: false, result: nil, error: .KeyNotFound) }) 
      }
   }
}

Here, the process is analogous to the one used to encrypt. We need to prepare the input and output buffers, and then, call SecKeyDecrypt. The exception here is that we need to adjust the resulting NSData length to the plainTextLen (by setting plainData.length = plainTextLen). This value is returned by SecKeyDecrypt and contains the exact length of the resulting decrypted data. If we don’t adjust the NSData, we will end up with trailing zeroes that will alter the resulting string.

Signing A Message And Verifying The Signature Scenario

Another common scenario for Asymmetric Cryptography is signing a message (more specifically, a hash of the message) with our private key. Then, we can send the message and the signature to another user. As a result, the recipient can check the signature to verify that the message actually comes from the sender. Also, you can also check that it has not been corrupted, altered or tainted.

For this scenario, we need to add two methods to our AsymmetricCryptoManager: one for signing a message and another one for verifying this signature.

Generating the Hash

In order to sign the message, we will use the function SecKeyRawSign, with the private key.

Generating a digital signature is an expensive process and can potentially take a lot of time for a long text. Thus, we will generate a SHA1 hash of the plain text and then generate a signature of that hash.

In order to do that, we need to call the CC_SHA1 method from CommonCrypto, that takes an input UnsafePointer<Void> buffer and writes its result in an output UnsafeMutablePointer<Void>.

These buffers can be generated like we did in the case of SecKeyEncrypt/SecKeyDecrypt. However, we should take into account that the input buffer should be UnsafePointer<Void> instead of UnsafePointer<UInt8>:

if let plainData = message.dataUsingEncoding(NSUTF8StringEncoding) {
   // generate hash of the plain data to sign
   guard let hashData = NSMutableData(length: Int(CC_SHA1_DIGEST_LENGTH)) 
   else {
      dispatch_async(dispatch_get_main_queue(), { completion(success: false, data: nil, error: .OutOfMemory) })
      return
   }
   let hash = UnsafeMutablePointer<UInt8>(hashData.mutableBytes)
   CC_SHA1(UnsafePointer<Void>(plainData.bytes), CC_LONG(plainData.length), hash)
   // ... now use the hash in the sign method.
}

Signing The Message

Now we can call SecKeyRawSign and return the resulting data on errSecSuccess. We need to use the private key reference obtained by means of getPrivateKeyReference():

let status = SecKeyRawSign(privateKeyRef, SecPadding.PKCS1SHA1, hash, hashData.length, resultPointer, &resultLength)

Note how we need to specify PKCS1SHA1 SecPadding to indicate SecKeyRawSign that we have used a SHA1 hash in the input data to sign.

Verifying The Signature

For the purpose of verifying the signature, we need to generate a hash with the exact same hashing algorithm that was used for the signature and then call SecKeyRawVerify. We need to use the public key reference obtained by means of getPublicKeyReference():

let status = SecKeyRawVerify(publicKeyRef, SecPadding.PKCS1SHA1, hash, Int(CC_SHA1_DIGEST_LENGTH), signaturePointer, signatureLength)

AsymmetricCryptoManager.

Asymmetric cryptographyIn the Github repository, you can find AsymmetricCryptoManager, the Asymmetric Cryptography manager, along with a sample view controller to test everything. As shown above, you can use it to perform basic Asymmetric Cryptography operations like encryption, decryption and signature verification.

 

No, iOS doesn’t like your OpenSSL keys

If you want to import keys generated outside iOS (maybe a public OpenSSL key from your server backend), the Security framework is not your friend. It requires some extra cooking for the keys to be imported and used properly in iOS.

Even though adding a valid OpenSSL public key using SecItemAdd will succeed (mysteriously), any further attempt to get that key with SecItemCopyMatching will return a nil reference (even though the status code will tell you that everything’s OK).

I will talk about how to import and use these external keys in iOS in the next episode.

Where to go from here?

In this episode of the “Cryptography in iOS” tutorial series, we learned about Asymmetric Cryptography in Swift. Also, we addressed the most common asymmetric cryptography scenarios, like encrypting, decrypting and verifying signatures. Next, in the third episode of the series, we will see how to import and export keys between iOS and other cryptographic systems (like OpenSSL).