What is a valid method to securely provide all instances of an app across different devices with a public/private key pair that would be used both to encrypt and decrypt data records created any of those devices, such that any instance of the app on any device can decrypt the records encrypted with those keys?
In particular, this is for a cloud-based iOS app on 9.3 or later that stores extremely sensitive information in some proprietary cloud-based servers. We don't want our own cloud server having the private key however, to eliminate the possibility of the key being leaked should our main server get hacked.
I was hoping there'd be a way for example to have Apple inject a common key to the system keychain at app install, so that no human outside of Apple's security fortress itself could see the private keys, and it would be as secure as the device's system keychain.
EDIT: More details on the method.
Step 1: Use OpenSSL to generate a set of keypairs, K1 through KN, where N is the total number of layers of encryption on the data, with the following command:
openssl genrsa -aes256 -out NPrivateKey.pem 2048 openssl rsa -in NPrivateKey.pem -out NPrivateKeyOpen.pem -outform PEM openssl rsa -in NPrivateKey.pem -pubout -out NPublicKey.pem -outform PEM
Step 2: Split up each key into a number F of separate files. These are the fragments.
Step 3: Encrypt each fragment of KN with KN+1 (K1 does not get encrypted).
Step 4: Create strategies S1 through SN for shuffling the order of the fragments of K1 through KN such that they can be algorithmically recombined into the original key by the app at runtime, assuming it knows N for a given K (how it knows N can be handled through an entirely separate process, if desired). This step allows for the fragments to exist in memory in a shuffled form for an additional degree of obscurity (for what it's worth). The goal here is simply to erect as many hurdles as possible in the path of a would-be attacker. For example if any aspects of the key were stored together on disk somewhere (for example in a repo or corporate key vault, etc.) this would tend to defeat simple attempts to scan a data volume, for example, using a regex sequence to identify keys stored on disk. If we are defeated, let it not be by a script kiddie—know what I'm saying?
Step 5: Create a strategy D1 through DN for the distribution of the fragments such that the app can collect all the fragments at runtime.
Step 6: Should any fragments be stored in the app's code itself they should be stored using the strongest possible form of obscurity, e.g. a set of purely algorithmic function calls that can output the key but that store no part of the key as a string literal. The strategy used for this obscurity would be O1 through ON, and which obscurity strategy is used for which fragment for which key is also shuffled according to strategy OS. (The use of method swizzling and dynamic runtime features of a language like Objective C can be useful here in creating some fun tricks to defeat anyone who gets a debugger attached to tell what the heck is going on; of course the distribution builds should always reject debugger connections, but that is out of scope for this answer.)
Step 7: All sections of the codebase implementing any aspects of the above strategies or storing any obscured key fragments should never ever appear in any commits to the primary code repository for the project. Git filters should be used in tandem with scripts (time for fun with Swift shells scripts!) to smudge and clean all such code. A completely separate repository should be maintained on an encrypted drive stored in a physical safe, that is only brought out when the relevant static library/objects need to be recompiled. That way even if someone gains access to the github account (or whatever) they will not gain access to the strategies or fragments.
Step 8: Do not store any keys in the system keychain. We have seen iOS's system keychain be compromised before. Other answers on StackOverflow can guide you on how to use a private key to decrypt data on iOS without the private key ever going into the system keychain. Perhaps this is a debatable point since the key could be deleted out of the keychain as soon as it's put in there, and for extra obscurity, you could do like we did and only store some of the keys in the stack in there, and store some of the encrypted fragments as if they were password hashes, etc. (all just more obscurity to lead people down false paths and confusing mazes that might try to break it).
Again, of course I have no doubt that it's technically possible to defeat this (or any) form of encryption; however the goal is simply to provide as steep of a hill to climb as is reasonably possible for a would-be attacker, yet ultimately have multiple clients be able to share the same private key.
Please refer to the article, Secure Key Distribution Using Fragmentation and Assimilation in Wireless Sensor and Actor Networks, which I discovered after developing the above solution. Ghafoor et al.'s "KDFA" method is in many respects similar to what I have described above.
Also, please note that I'm sure anyone implementing such a solution as this will realize that there are many points in the process where additional layers of obscurity and encryption can (and should) be interjected; the goal of this answer is merely to describe the broad strokes of the method. The devil is in the details, and precisely how this method is implemented could make it more, or less secure.
Also, of course, I believe it goes without saying that all network-based communication involved in the distribution strategies must be secured with TLS 1.2 w/PFS (or whatever the new best thing is when ever you are reading this in the future). Of course, further individual-client-specific private keys should be used on top of the shared private key. Of course, all the normal best practices in every regard are observed.
However the issue of how to distribute the same key to a group of devices such that only the clients, but not the server, can decrypt the data, is the problem, even if we follow all the best practices. What we have seen time and time again is that no matter how secure clients are, if someone owns the server then the fox gets all the eggs in the hen house. However if the server is merely a storage facility for totally encrypted data and the client employs a sufficiently dynamic and complex method to encrypt/decrypt that data, then it tends to help the concern of "what if the server gets owned" and "what if the code repository gets owned" etc.
Now if there are any major flaws in the above approach then of course, I would love to hear that so it can be improved.
I just ended up taking two keypairs. One of them I used to encrypt the other one. Then I broke up the encrypted form into many shards and stashed them all over the place... in the cloud... on servers... in obfuscated strings on my app... etc.
None of the strings involved in the encryption or keys is in string or binary format in the compiled app. It is all generated programatically in a quite obfuscated fashion.
At runtime we use arcane math functions to build method names that call hidden methods around the app to assemble the first key, then we go get the encrypted shards of the second key from all over the internet and decrypt them with the first key, then we use the second key to decrypt the important client data.
Then we use a special sauce method where none of the code that's involved in this obfuscation is anywhere in our repository. It gets loaded in dynamically at a special time. :D That's all I will say about that.