|
@@ -0,0 +1,111 @@
|
|
|
+package com.owncloud.android.utils;
|
|
|
+
|
|
|
+import android.app.KeyguardManager;
|
|
|
+import android.content.Context;
|
|
|
+import android.security.keystore.KeyGenParameterSpec;
|
|
|
+import android.security.keystore.KeyProperties;
|
|
|
+import android.support.annotation.RequiresApi;
|
|
|
+
|
|
|
+import com.owncloud.android.lib.common.utils.Log_OC;
|
|
|
+
|
|
|
+import java.io.IOException;
|
|
|
+import java.security.InvalidAlgorithmParameterException;
|
|
|
+import java.security.InvalidKeyException;
|
|
|
+import java.security.KeyStore;
|
|
|
+import java.security.KeyStoreException;
|
|
|
+import java.security.NoSuchAlgorithmException;
|
|
|
+import java.security.NoSuchProviderException;
|
|
|
+import java.security.UnrecoverableKeyException;
|
|
|
+import java.security.cert.CertificateException;
|
|
|
+
|
|
|
+import javax.crypto.BadPaddingException;
|
|
|
+import javax.crypto.Cipher;
|
|
|
+import javax.crypto.IllegalBlockSizeException;
|
|
|
+import javax.crypto.KeyGenerator;
|
|
|
+import javax.crypto.NoSuchPaddingException;
|
|
|
+import javax.crypto.SecretKey;
|
|
|
+
|
|
|
+/**
|
|
|
+ * Utility class with methods for handling device credentials.
|
|
|
+ */
|
|
|
+@RequiresApi(value = 23)
|
|
|
+public class DeviceCredentialUtils {
|
|
|
+
|
|
|
+ private static final String TAG = DeviceCredentialUtils.class.getSimpleName();
|
|
|
+
|
|
|
+ /** Alias for our key in the Android Key Store. */
|
|
|
+ private static final String KEY_NAME = "Nextcloud";
|
|
|
+ private static final byte[] SECRET_BYTE_ARRAY = new byte[] {1, 2, 3, 4, 5, 6};
|
|
|
+
|
|
|
+ private static final int AUTHENTICATION_DURATION_SECONDS = 30;
|
|
|
+
|
|
|
+ private static final String ANDROID_KEY_STORE = "AndroidKeyStore";
|
|
|
+
|
|
|
+ public static boolean areCredentialsAvailable(Context context) {
|
|
|
+ KeyguardManager keyguardManager = (KeyguardManager) context.getSystemService(
|
|
|
+ Context.KEYGUARD_SERVICE);
|
|
|
+ if (keyguardManager != null) {
|
|
|
+ return keyguardManager.isKeyguardSecure();
|
|
|
+ } else {
|
|
|
+ Log_OC.e(TAG, "Keyguard manager is null");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Creates a symmetric key in the Android Key Store which can only be used after the user has
|
|
|
+ * authenticated with device credentials within the last X seconds.
|
|
|
+ */
|
|
|
+ public static void createKey() {
|
|
|
+ // Generate a key to decrypt payment credentials, tokens, etc.
|
|
|
+ try {
|
|
|
+ KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE);
|
|
|
+ keyStore.load(null);
|
|
|
+ KeyGenerator keyGenerator = KeyGenerator.getInstance(
|
|
|
+ KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEY_STORE);
|
|
|
+
|
|
|
+ // Set the alias of the entry in Android KeyStore where the key will appear
|
|
|
+ // and the constrains (purposes) in the constructor of the Builder
|
|
|
+ keyGenerator.init(new KeyGenParameterSpec.Builder(KEY_NAME,
|
|
|
+ KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
|
|
|
+ .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
|
|
|
+ .setUserAuthenticationRequired(true)
|
|
|
+ // Require that the user has unlocked in the last 30 seconds
|
|
|
+ .setUserAuthenticationValidityDurationSeconds(AUTHENTICATION_DURATION_SECONDS)
|
|
|
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
|
|
|
+ .build());
|
|
|
+ keyGenerator.generateKey();
|
|
|
+ } catch (NoSuchAlgorithmException | NoSuchProviderException
|
|
|
+ | InvalidAlgorithmParameterException | KeyStoreException
|
|
|
+ | CertificateException | IOException e) {
|
|
|
+ Log_OC.e(TAG, "Exception: " + e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Tries to encrypt some data with the generated key in {@link #createKey} which
|
|
|
+ * only works if the user has just authenticated via device credentials.
|
|
|
+ */
|
|
|
+ public static boolean tryEncrypt() {
|
|
|
+ try {
|
|
|
+ KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE);
|
|
|
+ keyStore.load(null);
|
|
|
+ SecretKey secretKey = (SecretKey) keyStore.getKey(KEY_NAME, null);
|
|
|
+ Cipher cipher = Cipher.getInstance(
|
|
|
+ KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_CBC + "/"
|
|
|
+ + KeyProperties.ENCRYPTION_PADDING_PKCS7);
|
|
|
+
|
|
|
+ // Try encrypting something, it will only work if the user authenticated within
|
|
|
+ // the last AUTHENTICATION_DURATION_SECONDS seconds.
|
|
|
+ cipher.init(Cipher.ENCRYPT_MODE, secretKey);
|
|
|
+ cipher.doFinal(SECRET_BYTE_ARRAY);
|
|
|
+
|
|
|
+ // If the user has recently authenticated, you will reach here.
|
|
|
+ return true;
|
|
|
+ } catch (BadPaddingException | IllegalBlockSizeException | KeyStoreException |
|
|
|
+ CertificateException | UnrecoverableKeyException | IOException
|
|
|
+ | NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException e) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|