Class AndroidKeysetManager


  • public final class AndroidKeysetManager
    extends java.lang.Object
    A wrapper of KeysetManager that supports reading/writing Keyset to/from private shared preferences on Android.

    We don't recommend using this class. Instead, directly use AndroidKeystore.

    Warning

    This class reads and writes to shared preferences, thus is best not to run on the UI thread.

    Usage

    
     // One-time operations, should be done when the application is starting up.
     // Instead of repeatedly instantiating these crypto objects, instantiate them once and save for
     // later use.
     AndroidKeysetManager manager = AndroidKeysetManager.Builder()
        .withSharedPref(getApplicationContext(), "my_keyset_name", "my_pref_file_name")
        .withKeyTemplate(KeyTemplates.get("AES128_GCM_HKDF_4KB"))
        .build();
     StreamingAead streamingAead =
         manager.getKeysetHandle().getPrimitive(RegistryConfiguration.get(), StreamingAead.class);
     

    This will read a keyset stored in the my_keyset_name preference of the my_pref_file_name preferences file. If the preference file name is null, it uses the default preferences file.

    • If a keyset is found, but cannot be read, either an IOException or a GeneralSecurityException is thrown. The most common cause is that the master key is missing or the wrong master key is used. In this case, a GeneralSecurityException would be thrown. This is an irrecoverable error. You'd have to delete the keyset in Shared Preferences and all existing data encrypted with it.
    • If a keyset is not found, and a KeyTemplate is set with AndroidKeysetManager.Builder.withKeyTemplate(com.google.crypto.tink.KeyTemplate), a fresh keyset is generated and is written to the my_keyset_name preference of the my_pref_file_name shared preferences file.

    Adding a new key

    The resulting manager supports all operations supported by KeysetManager. For example to add a key to the keyset, you can do:

    
     manager.add(KeyTemplates.get("AES128_GCM_HKDF_4KB"));
     

    All operations that manipulate the keyset would automatically persist the new keyset to permanent storage.

    Opportunistic keyset encryption with Android Keystore

    Warning: because Android Keystore is unreliable, we strongly recommend disabling it by not setting any master key URI.

    If a master key URI is set with AndroidKeysetManager.Builder.withMasterKeyUri(java.lang.String), the keyset may be encrypted with a key generated and stored in Android Keystore.

    Android Keystore is only available on Android M or newer. Since it has been found that Android Keystore is unreliable on certain devices. Tink runs a self-test to detect such problems and disables Android Keystore accordingly, even if a master key URI is set. You can check whether Android Keystore is in use with isUsingKeystore().

    When Android Keystore is disabled or otherwise unavailable, keysets will be stored in cleartext. This is not as bad as it sounds because keysets remain inaccessible to any other apps running on the same device. Moreover, as of July 2020, most active Android devices support either full-disk encryption or file-based encryption, which provide strong security protection against key theft even from attackers with physical access to the device. Android Keystore is only useful when you want to require user authentication for key use, which should be done if and only if you're absolutely sure that Android Keystore is working properly on your target devices.

    The master key URI must start with android-keystore://. The remaining of the URI is used as a key ID when calling Android Keystore. If the master key doesn't exist, a fresh one is generated. If the master key already exists but is unusable, a KeyStoreException is thrown.

    This class is thread-safe.

    Since:
    1.0.0
    • Method Detail

      • getKeysetHandle

        public KeysetHandle getKeysetHandle()
                                     throws java.security.GeneralSecurityException
        Returns a KeysetHandle of the managed keyset.
        Throws:
        java.security.GeneralSecurityException
      • rotate

        @CanIgnoreReturnValue
        @Deprecated
        public AndroidKeysetManager rotate​(com.google.crypto.tink.proto.KeyTemplate keyTemplate)
                                    throws java.security.GeneralSecurityException
        Deprecated.
        Please use add(com.google.crypto.tink.proto.KeyTemplate). This method adds a new key and immediately promotes it to primary. However, when you do keyset rotation, you almost never want to make the new key primary, because old binaries don't know the new key yet.
        Generates and adds a fresh key generated using keyTemplate, and sets the new key as the primary key.
        Throws:
        java.security.GeneralSecurityException - if cannot find any KeyManager that can handle keyTemplate
      • add

        @CanIgnoreReturnValue
        public AndroidKeysetManager add​(com.google.crypto.tink.proto.KeyTemplate keyTemplate)
                                 throws java.security.GeneralSecurityException
        Generates and adds a fresh key generated using keyTemplate.
        Throws:
        java.security.GeneralSecurityException - if cannot find any KeyManager that can handle keyTemplate
      • add

        @CanIgnoreReturnValue
        public AndroidKeysetManager add​(KeyTemplate keyTemplate)
                                 throws java.security.GeneralSecurityException
        Generates and adds a fresh key generated using keyTemplate.
        Throws:
        java.security.GeneralSecurityException - if cannot find any KeyManager that can handle keyTemplate
      • setPrimary

        @CanIgnoreReturnValue
        public AndroidKeysetManager setPrimary​(int keyId)
                                        throws java.security.GeneralSecurityException
        Sets the key with keyId as primary.
        Throws:
        java.security.GeneralSecurityException - if the key is not found or not enabled
      • promote

        @InlineMe(replacement="this.setPrimary(keyId)")
        @CanIgnoreReturnValue
        @Deprecated
        public AndroidKeysetManager promote​(int keyId)
                                     throws java.security.GeneralSecurityException
        Deprecated.
        Sets the key with keyId as primary.
        Throws:
        java.security.GeneralSecurityException - if the key is not found or not enabled
      • enable

        @CanIgnoreReturnValue
        public AndroidKeysetManager enable​(int keyId)
                                    throws java.security.GeneralSecurityException
        Enables the key with keyId.
        Throws:
        java.security.GeneralSecurityException - if the key is not found
      • disable

        @CanIgnoreReturnValue
        public AndroidKeysetManager disable​(int keyId)
                                     throws java.security.GeneralSecurityException
        Disables the key with keyId.
        Throws:
        java.security.GeneralSecurityException - if the key is not found or it is the primary key
      • delete

        @CanIgnoreReturnValue
        public AndroidKeysetManager delete​(int keyId)
                                    throws java.security.GeneralSecurityException
        Deletes the key with keyId.
        Throws:
        java.security.GeneralSecurityException - if the key is not found or it is the primary key
      • destroy

        @CanIgnoreReturnValue
        public AndroidKeysetManager destroy​(int keyId)
                                     throws java.security.GeneralSecurityException
        Destroys the key material associated with the keyId.
        Throws:
        java.security.GeneralSecurityException - if the key is not found or it is the primary key
      • isUsingKeystore

        public boolean isUsingKeystore()
        Returns whether Android Keystore is being used to wrap Tink keysets.