Keys, or “credentials”, are a fact of life when working with REST APIs. Sign up for Amplitude for mobile analytics and you get a key to identify yourself and the application you’re working with. If you are using Aeris Weather APIs you get both an ID and secret. If you’re going to access these APIs in your iOS application you have to put those keys somewhere. In a previous post we walked through how to store API keys in property list files. In this tutorial we’re going to encrypt those keys and look at ways on how to access them in your app.
Before we get started, however, I want to make a few comments about security. I am not a security expert, and in all likelihood, neither are you. The original title of this article was Securing API Keys, but on reflection I renamed it to Obscuring API Keys. Nothing in this post will make the claim that this method of obscuring your API keys is impossible to break, because the reality is that anyone intent on stealing your API keys is going to do it. This method will only make it a little harder.
Encrypting Your Keys
To add another level of protection to our API keys, we’re going to use Blowfish Cipher Feedback encryption on them. To use this method of encryption you will want to choose:
- a secret
- an initialization vector
The secret must be at least 8 characters in length with a maximum of 56 characters. Choose whatever secret you like; a phrase, jumble of characters, it doesn’t matter. The initialization vector should be a random vector of 8 bytes. Once you’ve chosen these two pieces of data, download our Key Encrypter. Enter the secret and the IV, and then the API key you want to encrypt. For example, let’s say the API key we want to encrypt is 6bbb3654679105f33cf8c491ed9b04df
and we’ve chosen dontusethissecret
as our secret and decafbadbaddecaf
. Enter these values and click Encrypt.
The resulting output is a hexadecimal string that can be fed back through a decryption routine, along, of course, with the original secret and initialization vector.
Note: It is usually at this point someone objects and says, “but your secret and IV will be in your code.” That is true, they will. But your secret can be a substring of a text label in your application, your copyright notice, etc. Foolproof? No. Harder to disassemble. Yes.
Decrypting Your Key
The encryption and decryption routines make use of the Blowfish implementation included in mbed TLS. The routines are written in C and we leverage a bridging header to expose these routines in Swift.
Our decryption function in Swift 4.2 looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
func decrypt(message:[UInt8], key:String, iv:[UInt8]) -> String? { var context:mbedtls_blowfish_context = mbedtls_blowfish_context() let keybits = UInt32(key.count*8) // Initialize the Blowfish context and set the key mbedtls_blowfish_init(&context) mbedtls_blowfish_setkey(&context, key, keybits) var decryptIV:[UInt8] = iv var ivOffset = 0 var output:[UInt8] = [UInt8](repeating:0, count:message.count) if mbedtls_blowfish_crypt_cfb64(&context, MBEDTLS_BLOWFISH_DECRYPT, message.count, &ivOffset, &decryptIV, message, &output) == 0 { return String(bytes: output, encoding: String.Encoding.utf8) } else { return nil } } |
The question, now, is how to use the decryption method and where to put the secret and initialization vector in your code. This is up to you (and there are a variety of ways). Just remember that no matter what you do, if someone is determined to figure it out and extract it, they will. This is just another step they have to go through after decrypting and decompiling the app from a jailbroken phone, etc.
To test out decryption of the generated string, download this example iOS app, open in Xcode, and run it. You should see the original API key 6bbb3654679105f33cf8c491ed9b04df
.
Getting the Source
The source code for both the Mac encryption application and an example of how to decrypt an API key is available on Bitbucket:
Feel free to change out the encryption routines; Blowfish CFB is just one of many options to choose from.
Musings
To be honest, I posted this with some trepidation. Security is a topic that draws a lot of attention and frequently, well, developers with an attitude. How often have you heard the phrases:
- Security through obscurity isn’t!
- Never write your own cryptographic routine!
- If you don’t know what you’re doing then don’t!
- You’re just adding another level of indirection!
and so on. Yet at the same time we’re told to be more security conscious and program with it in mind. REST API keys for accessing services are provided to “everyday developers” and most documentation glosses over how to (more) securely embed them in your mobile app.
In the end, the author of DexGuard for Android sums up storing API keys on the mobile client nicely in a Stack Overflow post:
In the end, it’s an economic trade-off that you have to make: how important are the keys, how much time or software can you afford, how sophisticated are the hackers who are interested in the keys, how much time will they want to spend, how much worth is a delay before the keys are hacked, on what scale will any successful hackers distribute the keys, etc. Small pieces of information like keys are more difficult to protect than entire applications. Intrinsically, nothing on the client-side is unbreakable, but you can certainly raise the bar.” – Eric LaFortune
These are good guidelines to apply when determining what level of security you want to apply to your mobile applications. At some point you have to balance the likelihood of your keys being discovered against what you stand to lose if they do, and upon that decide how much time and effort you’re going to invest in obscuring them in your application.