1

I'm using 'androidx.security:security-crypto:1.1.0-alpha02' in an android project. It works fine for file encryption/decription. I've set up a master key inside the hardware keystore that requires authentication to be used.

private static MasterKey getOrCreateMasterKey(final Application application)
    throws IOException, GeneralSecurityException {
    return new MasterKey.Builder(application)
        .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
        .setUserAuthenticationRequired(true, 3600)
        .setRequestStrongBoxBacked(true)
        .build();
}

The Security API automatically generated what i think is a Tink keyset file, adapted for Android Shared Preferences.

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
    <string name="__androidx_security_crypto_encrypted_file_keyset__">129c0157ebfb4adffbd04e3ad4cd44336c5e89c3fe455d949031436c63789092960d8b93053de8e1fd855b61047a1d496ff26006958982e6a90f950746dbc7afc6b252bf51149b8404e5ff8616f9911ad54e153bf2c2c21eb571ca11223c77edd01b488465f7ab286ffd9054d7e396d1b2a187152dd9bf76ee5df9a5faefd5e5b7ec159fa04a860f9d237f833763f6c42acbcdedfb06a575264f948049b3841a4f08c094920612480a3d747970652e676f6f676c65617069732e636f6d2f676f6f676c652e63727970746f2e74696e6b2e41657347636d486b646653747265616d696e674b6579100118c09492062003</string>
</map>

If I understand correctly, that is the actual DEK used for encrypting the files.

Now the problem is that if I want to export an encrypted file I would not be able to decrypt on another device, because the master key inside the Hardware KeyStore is not exportable.

The best solution would be to export the DEK inside the keyset, with PBKDF2 and decrypt it into another device keyset.

An optional solution would be to create the master key material outside the keystore, protect it with another KeyStore key and export it with PBKDF2 when needed. This will break the logical flow of the jetpack Security library.

I've tried to import the Tink library 'com.google.crypto.tink:tink-android:1.5.0' so that I could start managing the Keyset directly, since the Security API doesn't have implementation for this usecase.

AndroidKeysetManager manager = new AndroidKeysetManager.Builder()
    .withSharedPref(getApplicationContext(), "my_keyset_name", "my_pref_file_name")
    .withKeyTemplate(AesGcmKeyManager.aes256GcmTemplate())
    .withMasterKeyUri(masterKeyUri)
    .build();

Anyway, I couldn't find a way to decrypt the key inside the keyset with this manager, in order to be able to encrypt it with a password and export it properly.

Do you know how should face this problem?

zjmo
  • 200
  • 6

1 Answers1

0

I've found the solution on the official "Tink HOW-TO": https://github.com/google/tink/blob/master/docs/JAVA-HOWTO.md

just little bit counterintuitive.

So here is a possible function to export a specific keyset previously encrypted with a masterkey generated inside the harware secure element with AndroidKeyStore:

 public byte[] exportKeyset(final String keysetName, final String password) throws
        IOException, GeneralSecurityException {


    AndroidKeysetManager backupKeySetManager = new AndroidKeysetManager.Builder()
            .withSharedPref(application, keysetName, "my_pref_file_name")
            .withKeyTemplate(AesGcmHkdfStreamingKeyManager.aes256GcmHkdf4KBTemplate())
            .withMasterKeyUri(MASTER_KEY_URI)
            .build();

    try(final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()){

        final byte[] saltBytes = new byte[16];
        new SecureRandom().nextBytes(saltBytes);

        final byte[] encryptionKeyMaterial =
                SecretKeyFactory.getInstance("PBKDF2withHmacSHA512")
                        .generateSecret(new PBEKeySpec(password.toCharArray(), saltBytes, 3000000, 256)).getEncoded();

        final AesGcmJce aesGcmJce = new AesGcmJce(encryptionKeyMaterial);

        CleartextKeysetHandle.write(backupKeySetManager.getKeysetHandle(),
                BinaryKeysetWriter.withOutputStream(byteArrayOutputStream));

        final byte[] clearKeyset = byteArrayOutputStream.toByteArray();

        byteArrayOutputStream.reset();
        byteArrayOutputStream.write(aesGcmJce.encrypt(clearKeyset, saltBytes));
        byteArrayOutputStream.write(saltBytes);

        return byteArrayOutputStream.toByteArray();

    }catch (IOException e){
        throw e;
    }

}
zjmo
  • 200
  • 6