Android and Java provide a crypto API that is relatively easy to use for crypto non-experts.
But since we know that no code can really be protected from reverse engineering, especially string constants used as seeds or shared secrets, I am wondering: What is the point of going through the ordeal of encrypting and decrypting in Android applications?
Am I missing something?
Trying to make my question clearer and more concrete: Suppose I have an application in which certain strings used by the code and in the code (i.e. not user data) need to be secret: One approach is to store them in encrypted form in the compiled
.apk and decrypt them (using an obfuscated hard-coded password) at runtime. Another approach would be to store them in encrypted form in a remote server, fetch them (over the Internet) and decrypt (using a shared password) them at runtime.
I don’t see much difference between the two, since both require the “secret key” being present in the (reverse-engineer-able) code.
Is there a solution to this problem?
If there isn’t a solution, why encrypt at all?
This is not strictly a problem with Android or Java. Anything can be reversed, it’s just harder if it’s native code. And bear in mind that they don’t even have to reverse it: you have to eventually decrypt the data on memory to manipulate it. At this point, the attacker can just take a memory dump and they will get your data. If they have physical access to the device, and you are manipulating the data in software, there is really nothing you can do to stop them. The solution for this is to use a dedicated hardware module (HSM) that is tamper-resistant or at least tamper-evident (if some one messes with it, it either deletes all data or at least keeps some logs of the event). Those come in different shapes and sizes ranging from smart cards to network connected devices that cost a lot. Currently not available for Android, but maybe it will get something similar to a TPM, so you can store your keys securely and do crypto operations in hardware.
So consider just how secret your data needs to be and decide on an adequate protection level.
You might want to have it downloaded it over SSL (that would protect it in transit), making sure you authenticate both the server (so you know you re getting the right data from a trusted place) and the client (so you can be sure you are only giving the data to the right person). You can use SSL client authentication for this, and it will be much more secure than any custom encryption/key exchange scheme you (or anyone who is not a cryptography expert) might come with.
The shared secret in the crypto API is not something that you would store in the app (as you say, that would be vulnerable to reverse-engineering — though perhaps not as vulnerable as you would expect; obfuscation is pretty easy).
Imagine instead you wanted to create/read encrypted files on your phone (for your secret grocery list).
After creating one, you save it using a master password (that is immediately discarded by the program). Then when you want to read it, you have to re-enter your master password. That’s the shared secret the API refers to, and it is completely tangential to reverse-engineering.
The problem you are describing is somewhat similar to storing a master password for a password manager problem.
In that case the solution offered is using salt for password hashes.
ateiob Any time you store the master password in the app you are really just making it a bit harder for unauthorized users to access the encrypted data.
First we can agree that encrypting data with a “master key” embedded in an application and storing that data on the phone is open to having the “master key” reverse engineered and the data decrypted.
Second I think we can agree that encrypting data with a secret password and then deleting the secret password should be reasonably safe using strong encryption, 256 bit keys and strong passwords. Both techniques apply to programming on mobile devices. In fact, iOS, supports BOTH needs out of the box.
[keychainData setObject:@"password" forKey:(id)kSecValueData];
Perhaps a real world example may help.
Say if on low memory a temporary data field must be persisted and protected, it can be encrypted with a master password and cleared when the user clears the temporary data field. The temporary data field is never stored as plain text.
So there are two passwords, a master password, embedded in the app for temporary short term encryption and a secret password, that usually must be entered by the user, for longer term persisted encrypted data.
Finally, if you are encrypting files, consider adding another level of indirection. So that the current secret password is used to encrypt a random key which is used to encrypt all the user’s files. This allows the user to change the secret password without any need to decrypt, encrypt all the encrypted files.
The attacker is assumed to have a copy of your code. The secrecy of your data should depend entirely on the key. See Kerckhoffs’s Principle.
To keep your key secret you must separate it from your code. Remember it. Keep it on a piece of paper in your wallet. Store it on a USB stick that you usually keep in a safe. Use a program like PasswordSafe. There are many possibilities.
It is of course possible to make any attacker work her way through many layers of keys to get to the key she actually needs. PasswordSafe and similar are one such option. You will notice that such programs do not give you an option to “remember your password” for you.