package com.greenaddress.greenbits;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.KeyguardManager;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore.KeyProperties;
import android.security.keystore.UserNotAuthenticatedException;
import android.util.Base64;
import com.greenaddress.greenapi.CryptoHelper;
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 java.security.spec.InvalidParameterSpecException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
public class KeyStoreAES {
private static final String KEYSTORE_KEY = "NativeAndroidAuth";
private static final int SECONDS_AUTH_VALID = 10;
private static final int ACTIVITY_REQUEST_CODE = 1;
@TargetApi(Build.VERSION_CODES.M)
public static void createKey(final boolean deleteImmediately) {
KeyStore keyStore = null;
try {
keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
final KeyGenerator keyGenerator = KeyGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
keyGenerator.init(new KeyGenParameterSpec.Builder(KEYSTORE_KEY,
KeyProperties.PURPOSE_ENCRYPT
| KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setUserAuthenticationRequired(true)
.setUserAuthenticationValidityDurationSeconds(SECONDS_AUTH_VALID)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
.build());
keyGenerator.generateKey();
} catch (final NoSuchAlgorithmException | NoSuchProviderException
| InvalidAlgorithmParameterException | KeyStoreException
| CertificateException | IOException e) {
throw new RuntimeException(e);
} finally {
if (deleteImmediately && keyStore != null) {
try {
keyStore.deleteEntry(KEYSTORE_KEY);
} catch (final KeyStoreException e) {
}
}
}
}
public static class RequiresAuthenticationScreen extends RuntimeException {}
public static class KeyInvalidated extends RuntimeException {}
@TargetApi(Build.VERSION_CODES.M)
public static String tryEncrypt(final GaService gaService) {
createKey(false);
final byte[] fakePin = CryptoHelper.randomBytes(32);
try {
final KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
final SecretKey secretKey = (SecretKey) keyStore.getKey(KEYSTORE_KEY, null);
final Cipher cipher = Cipher.getInstance(
android.security.keystore.KeyProperties.KEY_ALGORITHM_AES + '/'
+ android.security.keystore.KeyProperties.BLOCK_MODE_CBC + '/'
+ android.security.keystore.KeyProperties.ENCRYPTION_PADDING_PKCS7);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
final byte[] encryptedPIN = cipher.doFinal(fakePin);
final byte[] iv = cipher.getParameters().getParameterSpec(IvParameterSpec.class).getIV();
setPINConfig(gaService, Base64.encodeToString(encryptedPIN, Base64.NO_WRAP),
Base64.encodeToString(iv, Base64.NO_WRAP));
return Base64.encodeToString(fakePin, Base64.NO_WRAP).substring(0, 15);
} catch (final UserNotAuthenticatedException e) {
throw new RequiresAuthenticationScreen();
} catch (final KeyPermanentlyInvalidatedException e) {
throw new KeyInvalidated();
} catch (final InvalidParameterSpecException | BadPaddingException | IllegalBlockSizeException | KeyStoreException |
CertificateException | UnrecoverableKeyException | IOException
| NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException e) {
throw new RuntimeException(e);
}
}
private static void setPINConfig(final GaService gaService,
final String encryptedPIN, final String iv) {
gaService.cfgEdit("pin")
.putString("native", encryptedPIN)
.putString("nativeiv", iv)
.apply();
}
public static void wipePIN(final GaService gaService) {
// FIXME: Should we wipe the keystore value?
setPINConfig(gaService, "", "");
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public static void showAuthenticationScreen(final Activity act) {
final KeyguardManager keyguardManager = (KeyguardManager) act.getSystemService(Context.KEYGUARD_SERVICE);
final Intent intent = keyguardManager.createConfirmDeviceCredentialIntent(null, null);
if (intent != null) {
act.startActivityForResult(intent, ACTIVITY_REQUEST_CODE);
}
}
}