Cryptography In IOS (II): Asymmetric cryptography in Swift - Digital Leaves
1082
post-template-default,single,single-post,postid-1082,single-format-standard,qode-social-login-1.0,qode-restaurant-1.0,ajax_fade,page_not_loaded,,select-theme-ver-4.1,wpb-js-composer js-comp-ver-5.2,vc_responsive
Digital Leaves. Cryptography in iOS: Symmetric cryptography in Swift. Asymmetric Cryptography in Swift.

Cryptography In IOS (II): Asymmetric cryptography in Swift

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 cyphering 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 viceversa. 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 on 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:

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:

Generating our key pair.

The first thing we need to start operating within a 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 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.

This 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.

Here, 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:

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.

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:

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>:

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():

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():

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).

35 Comments
  • Randall Moore

    October 27, 2015 at 9:21 pm Reply

    Thanks for all your great work. It’s very well done, and very helpful.
    Can you add an MIT license to the source code for this example?

    • Ignacio Nieto Carvajal

      October 29, 2015 at 9:25 am Reply

      Thanks. The license is included in the repository README.md. Don’t worry, I won’t sue you if you use it or anything, I wrote this because I wanted to share it with the community.
      Best regards!

  • Edward Janne

    January 21, 2016 at 10:38 pm Reply

    Thank you for writing this tutorial; it certainly helped me to get started. I have a few comments that might help subsequent readers.

    To get it to work on OS X (10.7+), I had to declare SecKeyEncrypt, SecKeyDecrypt, SecKeyRawSign and SecKeyRawVerify in the bridging header because, for some reason, Apple has omitted them in the SDK headers, even though they have been linked into the framework.

    Also, I had to include the following parameters while generating the private key.

    kSecAttrCanEncrypt as String:true,
    kSecAttrCanDecrypt as String:false,
    kSecAttrCanSign as String:false,
    kSecAttrCanVerify as String:true

    And the following, for the private key.

    kSecAttrCanEncrypt as String:false,
    kSecAttrCanDecrypt as String:true,
    kSecAttrCanSign as String:true,
    kSecAttrCanVerify as String:false

    Otherwise, the decryption would fail.

    I also included the following when retrieving the private key.

    kSecAttrKeyClass as String:kSecAttrKeyClassPrivate,

    Although I’m not sure it’s absolutely necessary, it just seems symmetrical since there is analogous code when retrieving the public key.

    And finally, I notice that you explicitly call .destroy() on some of the memory pointers returned from NSMutableData. I’m wondering why this is necessary since these pointers are essentially just references to memory managed by NSMutableData and therefore under Foundation ARC management.

  • Ian

    June 23, 2016 at 4:09 pm Reply

    You are helping more than any other source found, sincere thanks.
    Just wandering if the code for RSA above is drastically different or majorly altered for ECC ?

    also i can’t seem to find the below post;

    “I will talk about how to import and use these external keys in iOS in another post soon.”

    What is the title of this post ?

    thanks again, you’re a legend.

    Ian

    • Ignacio Nieto Carvajal

      June 23, 2016 at 7:31 pm Reply

      Hello again Ian, I think by your other question that you already found the post, but in case you are still wondering, is this one: http://digitalleaves.com/blog/2015/10/asymmetric-cryptography-in-swift/

      Indeed, the code is mostly the same for using ECC, you just need to specify that key type (and proper length), but the operations and the rest of the Cryptography methods work exactly the same. Hope it helped!

    • anh thai

      December 8, 2016 at 8:50 am Reply

      I using ECC too, but t can’t sign and Encrypt. can you help me fix it.

      • Ignacio Nieto Carvajal

        December 8, 2016 at 7:57 pm Reply

        Hi there, did you changed in all methods the key type to “EC” ? You must change “kSecAttrKeyType as String: kSecAttrKeyTypeRSA,” to “kSecAttrKeyType as String: kSecAttrKeyTypeEC,”. Also, the key size for EC must be 256 or 384. Please, change that in “kAsymmetricCryptoManagerKeySize” and check on every place where you see a kSecAttrKeySizeInBits attribute.

        Hope it helps!

  • D.Simms

    July 26, 2016 at 9:13 am Reply

    i think you have a typo in the third paragraph… it says:

    “…so you can encrypt a message for a user with his private key, and only he will be able to decrypt it by using his private key.”

    it should say:

    “…so you can encrypt a message for a user with his public key, and only he will be able to decrypt it by using his private key.”

    i.e., you encrypt with his *public* key

    thanks for your work here

  • Tiago Martinho

    July 31, 2016 at 3:06 pm Reply

    Very nice post! Keep up the good work :D

  • Saif

    August 23, 2016 at 2:26 pm Reply

    Hey Ignacio,

    Thank you so much for this blog. I was wondering if you could maybe expand on what causes these errors and what should be done to properly handle them:
    var error = AsymmetricCryptoException.UnknownError
    switch (status) {
    case errSecDuplicateItem: error = .DuplicateFoundWhileTryingToCreateKey
    case errSecItemNotFound: error = .KeyNotFound
    case errSecAuthFailed: error = .AuthFailed
    default: break

    • Ignacio Nieto Carvajal

      September 5, 2016 at 7:11 am Reply

      Hi Saif, sorry for my late reply. I thought the error codes were kind of self-explaining, but you never know :)

      The meanings ar
      – DuplicateFoundWhileTryingToCreateKey: The key you are trying to create already exists in the keychain (by tag), so the operation fails.
      – KeyNotFound: The key you were trying to access with SecKeyItem… does not exists.
      – AuthFailed: This error indicates that the authentication failed, maybe the user fingerprint does not match, or any other authentication access occurred while trying to access the keys in keychain.

  • Jozef Matus

    August 25, 2016 at 9:06 am Reply

    Thank you a lot! This helped a lot

  • Harish

    September 29, 2016 at 7:23 am Reply

    Hey Ignacio,
    Very nice post, it helped me alot.
    It is working perfectly for strings. I would like to encrypt image with this algorithm. I tried
    func encryptImage() {
    let image = UIImage(named: “photo”)
    let imageData:NSData = UIImagePNGRepresentation(image!)!
    let strBase64:String = imageData.base64EncodedStringWithOptions(.Encoding64CharacterLineLength)
    AsymmetricCryptoManager.sharedInstance.encryptMessageWithPublicKey(strBase64) { (success, data, error) -> Void in
    if success {
    print(“success”)
    } else {
    print(error)
    }
    }
    }

    But it is throwing error “AsymmetricCrypto.AsymmetricCryptoException.UnableToEncrypt”
    Could you please help me?

    And also I would like to get public key from backend(java) and use it to encrypt strings/images for service calls. How to store public key(which I got from backend) in keychain and use it to encrypt?

    • Ignacio Nieto Carvajal

      October 12, 2016 at 8:18 pm Reply

      Hi Harish,
      Probably your image data is too long to be encrypted in a single chunk. RSA, for instance, has a limitation of 256 bytes. Given that each chunk grows, the actual limit for RSA would be around 245 bytes. Try splitting the image in chunks and encrypting them, then sending them to the other end and decrypting them in sequence, concatenating the results.

      Regarding the public key, as I specify in this post, you need the certificate unfortunately, in order to import the java generated public key inside iOS. Try generating a full certificate, extracting the public/private keys for java usage, and then using the certificate to import it on iOS.

    • Satyam Tyagi

      November 10, 2016 at 1:56 pm Reply

      Hi Harish

      Try this
      https://github.com/satyamtyagi/swiftcrypto

      Since it uses AES for actual encryption, there is no limitation on size

      Satyam

  • Ramgopal

    October 28, 2016 at 4:49 am Reply

    Thanks for the nice tutorial, Ignacio. Do you have any idea of encrypting or decrypting the text using EC algorithm?

  • Satyam Tyagi

    November 10, 2016 at 5:15 am Reply

    I found this extremely useful Thank you
    I have created a version for iOS 10
    https://github.com/satyamtyagi/swiftcrypto
    1. This uses ECC instead of RSA
    2. It uses newer APIs and no need to use C CommonCrypto for SHA
    3. The message size is not limited as it uses AES
    4. This supports authentication via TouchId (before access to private key)
    5. This supports secure enclave (for signing only Apple does not allow encryption yet)
    6. I’ve tried to minimize use of threads (dispatch_async) except where absolutely necessary
    7. There is no need for entitlements for Keychain access

    Please let me know your thoughts

    I’ll try to do a blogpost explaining the code, when I do will post comment here

    Satyam

  • Fabio Benedetti

    November 28, 2016 at 8:18 am Reply

    Thanks a lots for the tutorial,
    I don’t know if my previous has been published, but i have tried you project and with text > 256byte give me and error.
    do you know why?
    Thanks

    • Ignacio Nieto Carvajal

      December 6, 2016 at 11:07 am Reply

      Hi Fabio. As I mentioned in my email to you, you should divide the input string into manageable chunks, cipher every chunk and then decipher and join them together in the destination end.

  • anh thai

    December 7, 2016 at 7:17 am Reply

    Thanks for all.
    I have a question. after create key pairs, private key and public key save in key chain. when I turn off application, this key pair is lost or still? if key pairs is lost, how to save key pairs in key chain when I turn off my application and I can use private key to sign when I turn on application in the next?
    thanks so much.

    • Ignacio Nieto Carvajal

      December 8, 2016 at 7:58 pm Reply

      Hi there Ahn
      The keypair will remain because it’s specified on the creation attribute “kSecAttrIsPermanent”:

      // private key parameters
      let privateKeyParams: [String: AnyObject] = [
      kSecAttrIsPermanent as String: true as AnyObject,
      kSecAttrApplicationTag as String: kAsymmetricCryptoManagerApplicationTag as AnyObject
      ]

      // private key parameters
      let publicKeyParams: [String: AnyObject] = [
      kSecAttrIsPermanent as String: true as AnyObject,
      kSecAttrApplicationTag as String: kAsymmetricCryptoManagerApplicationTag as AnyObject
      ]

  • anh thai

    December 8, 2016 at 7:10 am Reply

    I create key pairs with type kSecAttrKeyTypeEC but I cant get publickey with your function getPublicKeyData. please help me fix it

    • Ignacio Nieto Carvajal

      December 8, 2016 at 7:56 pm Reply

      Hi there, did you changed in the method “getPublicKeyData()” the key type to “EC” ? You must change “kSecAttrKeyType as String: kSecAttrKeyTypeRSA,” to “kSecAttrKeyType as String: kSecAttrKeyTypeEC,”.

      Hope it helps!

  • Harold S

    December 9, 2016 at 1:32 am Reply

    I strongly encourage anyone that would like to use ECC decryption in the Secure Enclave to file a Radar bug with Apple to add this functionality. I’ve talked with some engineers and thats the only way they can determine interest and investing the time.

    • Ignacio Nieto Carvajal

      December 9, 2016 at 6:48 am Reply

      Hi Harold. I happen to be using EC encryption/decryprion and signing/verifying successfully with EC keys on secure enclave in my passwordless auth system on iOS. Its definitely not easy and the Security framework is painful to use and poorly documented but can be done.

      Maybe not a radar, but certainly I agree that Apple needa to reconsider its Security framework and make it more friendly to developers.

  • Frank Bow

    January 3, 2017 at 9:51 pm Reply

    Great, Great post. I’m a newbie on IOS and was wondering if getting it to work on Xcode 7.2.1 would represent a big deal ? I’ve noticed errors due to the use of “Data” type as well as an extension to that type which seems to be the main cause of build failure.

    • Ignacio Nieto Carvajal

      January 4, 2017 at 2:06 pm Reply

      Hi there, thanks for your words. Yes, it’s perfectly possible, but you will need to change some things here and there. As you say, you will need to replace the Data swift type with NSData, but then you will probably need to modify the functions that extract the bytes for encryption/decryption to use the old objective-C style methods. Any reason behind using that old Xcode version?

      By the way, if you want to keep up to date about new posts and swift tips, I recommend you to join the Digital Tips list (http://digitalleaves.us14.list-manage2.com/subscribe?u=d25fb11c7a202d0dfed1b3131&id=5186bd1975), I post content and swift tips every week. Thanks again and let me know if you have some other trouble adapting the code.

  • […] the next article, we will talk about asymmetric cryptography in Swift, and I will also share some useful classes and sample code ready to use for your projects. Did you […]

  • […] By now, you should have a good understanding of how Cryptography works in iOS. However, you might want to have a look at the previous post in the tutorial series: “Asymmetric Cryptography in iOS“. […]

  • Paul Von Schrottky

    June 30, 2017 at 1:51 pm Reply

    Great writeup 👍👌 Thanks a lot!

  • Paul Von Schrottky

    June 30, 2017 at 1:52 pm Reply

    Great writeup 👍👌 Thanks a lot mate!

  • Ajit Nadig

    August 11, 2017 at 5:21 am Reply

    Hi. thanks for your post. The documentation and code are good.
    I need to share the public and private key pairs to my server team for testing whether the message i encrypted can be decrypted at their end using generated keys.

    I am able to log the public key generated. But when I try to log the private key in decryptMessageWithPrivateKey method, it just prints the address and not the key data. Can you tell me how I can extract the private key being generated?

    Thanks

Post a Comment

Before you continue...

Hi there! I created the Digital Tips List, the newsletter for Swift and iOS developers, to share my knowledge with you.


It features exclusive weekly tutorials on Swift and iOS development, Swift code snippets and the Digital Tip of the week.


Besides, If you join the list you'll receive the eBook: "Your First iOS App", that will teach you how to build your first iOS App in less than an hour with no prior Swift or iOS knowledge.

I hate spam, and I promise I'll keep your email address safe.