androidandroid-ndk

Secure socket communication between Android apps


I have an standard Android app A and a second Android app B which has a shared library (.so/NDK). I want to setup a secure communication protocol between the two of them. I thought about using a simple localhost socket and use mTLS certificates to secure the channel. Is there a way to store certificates safely in the Android system so that nobody else can read it? I've read about key-attestation but do not have experience with that.

Would there be an alternative to communicate between a standard Android App and a NDK/.so library? I tried to used AIDL, could also generate the headers. But it appears as if NDK is lacking the libraries for setting up the bound service (only AOSP has the libraries). So I was not able to set up a bound service client with only using NDK.


Solution

  • TLS between apps could work but it's complex since you need a way to securely issue certificates to your client app; managing certificates and key storage is needlessly complex, plus it adds the entire network stack into the picture.

    There is definitely a way to use binder. If these are two standard Android apps then there is always going to be Java entry points available such as Service and ContentProvider from which you can then drop down into native.

    I am huge fan of ContentProvider#call for Android IPC, no need to bind to a service.

    Steps are:

    1. Make a subclass of ContentProvider in one of the processes, specifically implement the call method. Make sure you declare and export the component in the AndroidManifest and give it a unique authority.
    2. In the body of call immediately call Binder#getCallingUid(): https://developer.android.com/reference/android/os/Binder#getCallingUid()
    3. Using the UID get the associated package from PackageManager using PackageManager#getPackagesForUid(): https://developer.android.com/reference/android/content/pm/PackageManager.html?hl=en#getPackagesForUid(int)
    4. Get the "signatures" (misnomer, they are really certificates) from the calling package: https://developer.android.com/reference/androidx/core/content/pm/PackageInfoCompat#getSignatures(android.content.pm.PackageManager,java.lang.String)
    5. Verify the certificate of the calling package is identical byte-for-byte to your certificate (note in production and dev there will be different certificates!). Because Android has already verified the signature of the APK matches the certificate during installation you can trust this means the calling app was signed with the owner of the private key of that certificate (you).
    6. In the client app use ContentProviderClient to call into the app hosting the ContentProvider using your declared authority.

    You can drop down to JNI from the ContentProvider#call method implementation and/or you can call up from native to Java to invoke the ContentResolver#call method as needed.

    It might be possible to implement some of the above natively though might not be worth the effort, see https://github.com/lakinduboteju/AndroidNdkBinderExamples for some NDK binder examples.