Giper
Giper

Reputation: 53

Why command line dalvikvm uses standard Java security libraries (keystore) instead of using the Android version

I am performing an experiment: using Android keystore in a command-line Java application. I have an Activity hello world example of the keystore:
https://github.com/phanirajabhandari/android-keystore-example

I have just converted private method getKeyStore() in EncryptionUtils and added a single line on MainActivity, to print the getKeyStore(). The MainActivity is as follows:

public class MainActivity extends AppCompatActivity {

  @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Timber.plant(new Timber.DebugTree());

    String value = "Password/Token to be encrypted";

    String encryptedValue = EncryptionUtils.encrypt(this, value);
    Timber.d(" Encrypted Value :" + encryptedValue);

    String decryptedValue = EncryptionUtils.decrypt(this, encryptedValue);
    Timber.d(" Decrypted Value :" + decryptedValue);
    
    Timber.d("Keystore:" + EncryptionUtils.getKeyStore());
  }
}

getKeyStore() method simply returns the Android Keystore:

  public static KeyStore getKeyStore() {
    KeyStore keyStore = null;
    try {
      keyStore = KeyStore.getInstance(EncryptionKeyGenerator.ANDROID_KEY_STORE);
      keyStore.load(null);
      //FileOutputStream fos = new FileOutputStream("/data/data/com.example.keystore/mykeystore");
      //keyStore.store(fos, "".toCharArray());
    } catch (KeyStoreException | CertificateException | NoSuchAlgorithmException | IOException e) {
      Timber.e(e);
    }
    return keyStore;
  }

I build the apk by using Android Studio and the Gradle provided by the project, and on the logs I can see that Keystore object is properly created:

2021-01-09 10:51:53.246 4103-4103/com.example.keystore D/MainActivity:  Encrypted Value :fH92kpVlBIRTVF3SnTiSZejZtDRa0H2uj1Ze2kXJ8Obt9OEO5_Qd0RNVlAkK8Q==
2021-01-09 10:51:53.272 4103-4103/com.example.keystore D/MainActivity:  Decrypted Value :Password/Token to be encrypted
2021-01-09 10:51:53.275 4103-4103/com.example.keystore D/MainActivity: Keystore:java.security.KeyStore@afbdfae

Now I am trying to following experiment:

  1. Create a MainExample.java class:
package com.example.keystore;

public class MainExample {
    public static void main(String[] args) {
       System.out.println("Hello") ;
        System.out.println(EncryptionUtils.getKeyStore()) ;
    }
}
  1. build and upload apk in android emulator (genymotion, root access):
adb push app-debug.apk /data/local/tmp/   
  1. Execute the main hello world by using dalvikvm
dalvikvm -cp app-debug.apk com.example.keystore.MainExample                       <
Hello
java.security.KeyStoreException: AndroidKeyStore not found
        at java.security.KeyStore.getInstance(KeyStore.java:890)
        at com.example.keystore.EncryptionUtils.getKeyStore(EncryptionUtils.java:40)
        at com.example.keystore.MainExample.main(MainExample.java:6)
Caused by: java.security.NoSuchAlgorithmException: AndroidKeyStore KeyStore not available
        at sun.security.jca.GetInstance.getInstance(GetInstance.java:159)
        at java.security.Security.getImpl(Security.java:628)
        at java.security.KeyStore.getInstance(KeyStore.java:887)
        ... 2 more
null  

It seems that AndroidKeyStore is not seen by dalvikvm, as if dalvikvm is not using Android "java.security" package but official Java package. Why dalvikvm command-line execution is different by the Activity execution? Thank you in advance.

Upvotes: 1

Views: 284

Answers (1)

JacobTDC
JacobTDC

Reputation: 485

The AndroidKeyStore Provider is usually installed by the system early on in the app lifecycle. You'll need to call the install() function yourself. There are a couple of ways to do this.

Using a Stub:

You'll need to make a new gradle subproject named something like "hidden-api" with a stub, like so:

package android.security.keystore2;

public class AndroidKeyStoreProvider {
  public static void install() {
    throw new RuntimeException("STUB");
  }
}

Then, add a dependency to your main project like this:

dependencies {
  compileOnly project(":hidden-api")
}

Then, before you try to use the AndroidKeyStore Provider, call android.security.keystore2.AndroidKeyStoreProvider.install().

Using Reflection:

Using reflection requires no subprojects, you simply have to add this snippet before you use the AndroidKeyStore Provider:

try {
  Class.forName("android.security.keystore2.AndroidKeyStoreProvider")
    .getMethod("install")
    .invoke(null);
} catch (Exception e) {
  throw new RuntimeException(e);
}

Note:

In both cases, however, you will not be able to use biometrics or user authentication, because doing so causes the AndroidKeyStore Provider to attempt to get the current application from the ActivityThread (by calling AppGlobals.getInitialApplication(), which in turn calls ActivityThread.currentApplication()), which has no application associated with it.

Upvotes: 0

Related Questions