1*90c8c64dSAndroid Build Coastguard Worker /* 2*90c8c64dSAndroid Build Coastguard Worker * Copyright (C) 2015 The Android Open Source Project 3*90c8c64dSAndroid Build Coastguard Worker * 4*90c8c64dSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License"); 5*90c8c64dSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License. 6*90c8c64dSAndroid Build Coastguard Worker * You may obtain a copy of the License at 7*90c8c64dSAndroid Build Coastguard Worker * 8*90c8c64dSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0 9*90c8c64dSAndroid Build Coastguard Worker * 10*90c8c64dSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software 11*90c8c64dSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS, 12*90c8c64dSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*90c8c64dSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and 14*90c8c64dSAndroid Build Coastguard Worker * limitations under the License 15*90c8c64dSAndroid Build Coastguard Worker */ 16*90c8c64dSAndroid Build Coastguard Worker 17*90c8c64dSAndroid Build Coastguard Worker package com.example.android.fingerprintdialog; 18*90c8c64dSAndroid Build Coastguard Worker 19*90c8c64dSAndroid Build Coastguard Worker import android.app.Activity; 20*90c8c64dSAndroid Build Coastguard Worker import android.app.KeyguardManager; 21*90c8c64dSAndroid Build Coastguard Worker import android.content.Intent; 22*90c8c64dSAndroid Build Coastguard Worker import android.content.SharedPreferences; 23*90c8c64dSAndroid Build Coastguard Worker import android.hardware.fingerprint.FingerprintManager; 24*90c8c64dSAndroid Build Coastguard Worker import android.os.Build; 25*90c8c64dSAndroid Build Coastguard Worker import android.os.Bundle; 26*90c8c64dSAndroid Build Coastguard Worker import android.preference.PreferenceManager; 27*90c8c64dSAndroid Build Coastguard Worker import android.security.keystore.KeyGenParameterSpec; 28*90c8c64dSAndroid Build Coastguard Worker import android.security.keystore.KeyPermanentlyInvalidatedException; 29*90c8c64dSAndroid Build Coastguard Worker import android.security.keystore.KeyProperties; 30*90c8c64dSAndroid Build Coastguard Worker import android.support.annotation.Nullable; 31*90c8c64dSAndroid Build Coastguard Worker import android.util.Base64; 32*90c8c64dSAndroid Build Coastguard Worker import android.util.Log; 33*90c8c64dSAndroid Build Coastguard Worker import android.view.Menu; 34*90c8c64dSAndroid Build Coastguard Worker import android.view.MenuItem; 35*90c8c64dSAndroid Build Coastguard Worker import android.view.View; 36*90c8c64dSAndroid Build Coastguard Worker import android.widget.Button; 37*90c8c64dSAndroid Build Coastguard Worker import android.widget.TextView; 38*90c8c64dSAndroid Build Coastguard Worker import android.widget.Toast; 39*90c8c64dSAndroid Build Coastguard Worker 40*90c8c64dSAndroid Build Coastguard Worker import java.io.IOException; 41*90c8c64dSAndroid Build Coastguard Worker import java.security.InvalidAlgorithmParameterException; 42*90c8c64dSAndroid Build Coastguard Worker import java.security.InvalidKeyException; 43*90c8c64dSAndroid Build Coastguard Worker import java.security.KeyStore; 44*90c8c64dSAndroid Build Coastguard Worker import java.security.KeyStoreException; 45*90c8c64dSAndroid Build Coastguard Worker import java.security.NoSuchAlgorithmException; 46*90c8c64dSAndroid Build Coastguard Worker import java.security.NoSuchProviderException; 47*90c8c64dSAndroid Build Coastguard Worker import java.security.UnrecoverableKeyException; 48*90c8c64dSAndroid Build Coastguard Worker import java.security.cert.CertificateException; 49*90c8c64dSAndroid Build Coastguard Worker 50*90c8c64dSAndroid Build Coastguard Worker import javax.crypto.BadPaddingException; 51*90c8c64dSAndroid Build Coastguard Worker import javax.crypto.Cipher; 52*90c8c64dSAndroid Build Coastguard Worker import javax.crypto.IllegalBlockSizeException; 53*90c8c64dSAndroid Build Coastguard Worker import javax.crypto.KeyGenerator; 54*90c8c64dSAndroid Build Coastguard Worker import javax.crypto.NoSuchPaddingException; 55*90c8c64dSAndroid Build Coastguard Worker import javax.crypto.SecretKey; 56*90c8c64dSAndroid Build Coastguard Worker 57*90c8c64dSAndroid Build Coastguard Worker /** 58*90c8c64dSAndroid Build Coastguard Worker * Main entry point for the sample, showing a backpack and "Purchase" button. 59*90c8c64dSAndroid Build Coastguard Worker */ 60*90c8c64dSAndroid Build Coastguard Worker public class MainActivity extends Activity { 61*90c8c64dSAndroid Build Coastguard Worker 62*90c8c64dSAndroid Build Coastguard Worker private static final String TAG = MainActivity.class.getSimpleName(); 63*90c8c64dSAndroid Build Coastguard Worker 64*90c8c64dSAndroid Build Coastguard Worker private static final String DIALOG_FRAGMENT_TAG = "myFragment"; 65*90c8c64dSAndroid Build Coastguard Worker private static final String SECRET_MESSAGE = "Very secret message"; 66*90c8c64dSAndroid Build Coastguard Worker private static final String KEY_NAME_NOT_INVALIDATED = "key_not_invalidated"; 67*90c8c64dSAndroid Build Coastguard Worker static final String DEFAULT_KEY_NAME = "default_key"; 68*90c8c64dSAndroid Build Coastguard Worker 69*90c8c64dSAndroid Build Coastguard Worker private KeyStore mKeyStore; 70*90c8c64dSAndroid Build Coastguard Worker private KeyGenerator mKeyGenerator; 71*90c8c64dSAndroid Build Coastguard Worker private SharedPreferences mSharedPreferences; 72*90c8c64dSAndroid Build Coastguard Worker 73*90c8c64dSAndroid Build Coastguard Worker @Override onCreate(Bundle savedInstanceState)74*90c8c64dSAndroid Build Coastguard Worker protected void onCreate(Bundle savedInstanceState) { 75*90c8c64dSAndroid Build Coastguard Worker super.onCreate(savedInstanceState); 76*90c8c64dSAndroid Build Coastguard Worker setContentView(R.layout.activity_main); 77*90c8c64dSAndroid Build Coastguard Worker 78*90c8c64dSAndroid Build Coastguard Worker try { 79*90c8c64dSAndroid Build Coastguard Worker mKeyStore = KeyStore.getInstance("AndroidKeyStore"); 80*90c8c64dSAndroid Build Coastguard Worker } catch (KeyStoreException e) { 81*90c8c64dSAndroid Build Coastguard Worker throw new RuntimeException("Failed to get an instance of KeyStore", e); 82*90c8c64dSAndroid Build Coastguard Worker } 83*90c8c64dSAndroid Build Coastguard Worker try { 84*90c8c64dSAndroid Build Coastguard Worker mKeyGenerator = KeyGenerator 85*90c8c64dSAndroid Build Coastguard Worker .getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore"); 86*90c8c64dSAndroid Build Coastguard Worker } catch (NoSuchAlgorithmException | NoSuchProviderException e) { 87*90c8c64dSAndroid Build Coastguard Worker throw new RuntimeException("Failed to get an instance of KeyGenerator", e); 88*90c8c64dSAndroid Build Coastguard Worker } 89*90c8c64dSAndroid Build Coastguard Worker Cipher defaultCipher; 90*90c8c64dSAndroid Build Coastguard Worker Cipher cipherNotInvalidated; 91*90c8c64dSAndroid Build Coastguard Worker try { 92*90c8c64dSAndroid Build Coastguard Worker defaultCipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/" 93*90c8c64dSAndroid Build Coastguard Worker + KeyProperties.BLOCK_MODE_CBC + "/" 94*90c8c64dSAndroid Build Coastguard Worker + KeyProperties.ENCRYPTION_PADDING_PKCS7); 95*90c8c64dSAndroid Build Coastguard Worker cipherNotInvalidated = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/" 96*90c8c64dSAndroid Build Coastguard Worker + KeyProperties.BLOCK_MODE_CBC + "/" 97*90c8c64dSAndroid Build Coastguard Worker + KeyProperties.ENCRYPTION_PADDING_PKCS7); 98*90c8c64dSAndroid Build Coastguard Worker } catch (NoSuchAlgorithmException | NoSuchPaddingException e) { 99*90c8c64dSAndroid Build Coastguard Worker throw new RuntimeException("Failed to get an instance of Cipher", e); 100*90c8c64dSAndroid Build Coastguard Worker } 101*90c8c64dSAndroid Build Coastguard Worker mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); 102*90c8c64dSAndroid Build Coastguard Worker 103*90c8c64dSAndroid Build Coastguard Worker KeyguardManager keyguardManager = getSystemService(KeyguardManager.class); 104*90c8c64dSAndroid Build Coastguard Worker FingerprintManager fingerprintManager = getSystemService(FingerprintManager.class); 105*90c8c64dSAndroid Build Coastguard Worker Button purchaseButton = (Button) findViewById(R.id.purchase_button); 106*90c8c64dSAndroid Build Coastguard Worker Button purchaseButtonNotInvalidated = (Button) findViewById( 107*90c8c64dSAndroid Build Coastguard Worker R.id.purchase_button_not_invalidated); 108*90c8c64dSAndroid Build Coastguard Worker 109*90c8c64dSAndroid Build Coastguard Worker if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 110*90c8c64dSAndroid Build Coastguard Worker purchaseButtonNotInvalidated.setEnabled(true); 111*90c8c64dSAndroid Build Coastguard Worker purchaseButtonNotInvalidated.setOnClickListener( 112*90c8c64dSAndroid Build Coastguard Worker new PurchaseButtonClickListener(cipherNotInvalidated, 113*90c8c64dSAndroid Build Coastguard Worker KEY_NAME_NOT_INVALIDATED)); 114*90c8c64dSAndroid Build Coastguard Worker } else { 115*90c8c64dSAndroid Build Coastguard Worker // Hide the purchase button which uses a non-invalidated key 116*90c8c64dSAndroid Build Coastguard Worker // if the app doesn't work on Android N preview 117*90c8c64dSAndroid Build Coastguard Worker purchaseButtonNotInvalidated.setVisibility(View.GONE); 118*90c8c64dSAndroid Build Coastguard Worker findViewById(R.id.purchase_button_not_invalidated_description) 119*90c8c64dSAndroid Build Coastguard Worker .setVisibility(View.GONE); 120*90c8c64dSAndroid Build Coastguard Worker } 121*90c8c64dSAndroid Build Coastguard Worker 122*90c8c64dSAndroid Build Coastguard Worker if (!keyguardManager.isKeyguardSecure()) { 123*90c8c64dSAndroid Build Coastguard Worker // Show a message that the user hasn't set up a fingerprint or lock screen. 124*90c8c64dSAndroid Build Coastguard Worker Toast.makeText(this, 125*90c8c64dSAndroid Build Coastguard Worker "Secure lock screen hasn't set up.\n" 126*90c8c64dSAndroid Build Coastguard Worker + "Go to 'Settings -> Security -> Fingerprint' to set up a fingerprint", 127*90c8c64dSAndroid Build Coastguard Worker Toast.LENGTH_LONG).show(); 128*90c8c64dSAndroid Build Coastguard Worker purchaseButton.setEnabled(false); 129*90c8c64dSAndroid Build Coastguard Worker purchaseButtonNotInvalidated.setEnabled(false); 130*90c8c64dSAndroid Build Coastguard Worker return; 131*90c8c64dSAndroid Build Coastguard Worker } 132*90c8c64dSAndroid Build Coastguard Worker 133*90c8c64dSAndroid Build Coastguard Worker // Now the protection level of USE_FINGERPRINT permission is normal instead of dangerous. 134*90c8c64dSAndroid Build Coastguard Worker // See http://developer.android.com/reference/android/Manifest.permission.html#USE_FINGERPRINT 135*90c8c64dSAndroid Build Coastguard Worker // The line below prevents the false positive inspection from Android Studio 136*90c8c64dSAndroid Build Coastguard Worker // noinspection ResourceType 137*90c8c64dSAndroid Build Coastguard Worker if (!fingerprintManager.hasEnrolledFingerprints()) { 138*90c8c64dSAndroid Build Coastguard Worker purchaseButton.setEnabled(false); 139*90c8c64dSAndroid Build Coastguard Worker // This happens when no fingerprints are registered. 140*90c8c64dSAndroid Build Coastguard Worker Toast.makeText(this, 141*90c8c64dSAndroid Build Coastguard Worker "Go to 'Settings -> Security -> Fingerprint' and register at least one fingerprint", 142*90c8c64dSAndroid Build Coastguard Worker Toast.LENGTH_LONG).show(); 143*90c8c64dSAndroid Build Coastguard Worker return; 144*90c8c64dSAndroid Build Coastguard Worker } 145*90c8c64dSAndroid Build Coastguard Worker createKey(DEFAULT_KEY_NAME, true); 146*90c8c64dSAndroid Build Coastguard Worker createKey(KEY_NAME_NOT_INVALIDATED, false); 147*90c8c64dSAndroid Build Coastguard Worker purchaseButton.setEnabled(true); 148*90c8c64dSAndroid Build Coastguard Worker purchaseButton.setOnClickListener( 149*90c8c64dSAndroid Build Coastguard Worker new PurchaseButtonClickListener(defaultCipher, DEFAULT_KEY_NAME)); 150*90c8c64dSAndroid Build Coastguard Worker } 151*90c8c64dSAndroid Build Coastguard Worker 152*90c8c64dSAndroid Build Coastguard Worker /** 153*90c8c64dSAndroid Build Coastguard Worker * Initialize the {@link Cipher} instance with the created key in the 154*90c8c64dSAndroid Build Coastguard Worker * {@link #createKey(String, boolean)} method. 155*90c8c64dSAndroid Build Coastguard Worker * 156*90c8c64dSAndroid Build Coastguard Worker * @param keyName the key name to init the cipher 157*90c8c64dSAndroid Build Coastguard Worker * @return {@code true} if initialization is successful, {@code false} if the lock screen has 158*90c8c64dSAndroid Build Coastguard Worker * been disabled or reset after the key was generated, or if a fingerprint got enrolled after 159*90c8c64dSAndroid Build Coastguard Worker * the key was generated. 160*90c8c64dSAndroid Build Coastguard Worker */ initCipher(Cipher cipher, String keyName)161*90c8c64dSAndroid Build Coastguard Worker private boolean initCipher(Cipher cipher, String keyName) { 162*90c8c64dSAndroid Build Coastguard Worker try { 163*90c8c64dSAndroid Build Coastguard Worker mKeyStore.load(null); 164*90c8c64dSAndroid Build Coastguard Worker SecretKey key = (SecretKey) mKeyStore.getKey(keyName, null); 165*90c8c64dSAndroid Build Coastguard Worker cipher.init(Cipher.ENCRYPT_MODE, key); 166*90c8c64dSAndroid Build Coastguard Worker return true; 167*90c8c64dSAndroid Build Coastguard Worker } catch (KeyPermanentlyInvalidatedException e) { 168*90c8c64dSAndroid Build Coastguard Worker return false; 169*90c8c64dSAndroid Build Coastguard Worker } catch (KeyStoreException | CertificateException | UnrecoverableKeyException | IOException 170*90c8c64dSAndroid Build Coastguard Worker | NoSuchAlgorithmException | InvalidKeyException e) { 171*90c8c64dSAndroid Build Coastguard Worker throw new RuntimeException("Failed to init Cipher", e); 172*90c8c64dSAndroid Build Coastguard Worker } 173*90c8c64dSAndroid Build Coastguard Worker } 174*90c8c64dSAndroid Build Coastguard Worker 175*90c8c64dSAndroid Build Coastguard Worker /** 176*90c8c64dSAndroid Build Coastguard Worker * Proceed the purchase operation 177*90c8c64dSAndroid Build Coastguard Worker * 178*90c8c64dSAndroid Build Coastguard Worker * @param withFingerprint {@code true} if the purchase was made by using a fingerprint 179*90c8c64dSAndroid Build Coastguard Worker * @param cryptoObject the Crypto object 180*90c8c64dSAndroid Build Coastguard Worker */ onPurchased(boolean withFingerprint, @Nullable FingerprintManager.CryptoObject cryptoObject)181*90c8c64dSAndroid Build Coastguard Worker public void onPurchased(boolean withFingerprint, 182*90c8c64dSAndroid Build Coastguard Worker @Nullable FingerprintManager.CryptoObject cryptoObject) { 183*90c8c64dSAndroid Build Coastguard Worker if (withFingerprint) { 184*90c8c64dSAndroid Build Coastguard Worker // If the user has authenticated with fingerprint, verify that using cryptography and 185*90c8c64dSAndroid Build Coastguard Worker // then show the confirmation message. 186*90c8c64dSAndroid Build Coastguard Worker assert cryptoObject != null; 187*90c8c64dSAndroid Build Coastguard Worker tryEncrypt(cryptoObject.getCipher()); 188*90c8c64dSAndroid Build Coastguard Worker } else { 189*90c8c64dSAndroid Build Coastguard Worker // Authentication happened with backup password. Just show the confirmation message. 190*90c8c64dSAndroid Build Coastguard Worker showConfirmation(null); 191*90c8c64dSAndroid Build Coastguard Worker } 192*90c8c64dSAndroid Build Coastguard Worker } 193*90c8c64dSAndroid Build Coastguard Worker 194*90c8c64dSAndroid Build Coastguard Worker // Show confirmation, if fingerprint was used show crypto information. showConfirmation(byte[] encrypted)195*90c8c64dSAndroid Build Coastguard Worker private void showConfirmation(byte[] encrypted) { 196*90c8c64dSAndroid Build Coastguard Worker findViewById(R.id.confirmation_message).setVisibility(View.VISIBLE); 197*90c8c64dSAndroid Build Coastguard Worker if (encrypted != null) { 198*90c8c64dSAndroid Build Coastguard Worker TextView v = (TextView) findViewById(R.id.encrypted_message); 199*90c8c64dSAndroid Build Coastguard Worker v.setVisibility(View.VISIBLE); 200*90c8c64dSAndroid Build Coastguard Worker v.setText(Base64.encodeToString(encrypted, 0 /* flags */)); 201*90c8c64dSAndroid Build Coastguard Worker } 202*90c8c64dSAndroid Build Coastguard Worker } 203*90c8c64dSAndroid Build Coastguard Worker 204*90c8c64dSAndroid Build Coastguard Worker /** 205*90c8c64dSAndroid Build Coastguard Worker * Tries to encrypt some data with the generated key in {@link #createKey} which is 206*90c8c64dSAndroid Build Coastguard Worker * only works if the user has just authenticated via fingerprint. 207*90c8c64dSAndroid Build Coastguard Worker */ tryEncrypt(Cipher cipher)208*90c8c64dSAndroid Build Coastguard Worker private void tryEncrypt(Cipher cipher) { 209*90c8c64dSAndroid Build Coastguard Worker try { 210*90c8c64dSAndroid Build Coastguard Worker byte[] encrypted = cipher.doFinal(SECRET_MESSAGE.getBytes()); 211*90c8c64dSAndroid Build Coastguard Worker showConfirmation(encrypted); 212*90c8c64dSAndroid Build Coastguard Worker } catch (BadPaddingException | IllegalBlockSizeException e) { 213*90c8c64dSAndroid Build Coastguard Worker Toast.makeText(this, "Failed to encrypt the data with the generated key. " 214*90c8c64dSAndroid Build Coastguard Worker + "Retry the purchase", Toast.LENGTH_LONG).show(); 215*90c8c64dSAndroid Build Coastguard Worker Log.e(TAG, "Failed to encrypt the data with the generated key." + e.getMessage()); 216*90c8c64dSAndroid Build Coastguard Worker } 217*90c8c64dSAndroid Build Coastguard Worker } 218*90c8c64dSAndroid Build Coastguard Worker 219*90c8c64dSAndroid Build Coastguard Worker /** 220*90c8c64dSAndroid Build Coastguard Worker * Creates a symmetric key in the Android Key Store which can only be used after the user has 221*90c8c64dSAndroid Build Coastguard Worker * authenticated with fingerprint. 222*90c8c64dSAndroid Build Coastguard Worker * 223*90c8c64dSAndroid Build Coastguard Worker * @param keyName the name of the key to be created 224*90c8c64dSAndroid Build Coastguard Worker * @param invalidatedByBiometricEnrollment if {@code false} is passed, the created key will not 225*90c8c64dSAndroid Build Coastguard Worker * be invalidated even if a new fingerprint is enrolled. 226*90c8c64dSAndroid Build Coastguard Worker * The default value is {@code true}, so passing 227*90c8c64dSAndroid Build Coastguard Worker * {@code true} doesn't change the behavior 228*90c8c64dSAndroid Build Coastguard Worker * (the key will be invalidated if a new fingerprint is 229*90c8c64dSAndroid Build Coastguard Worker * enrolled.). Note that this parameter is only valid if 230*90c8c64dSAndroid Build Coastguard Worker * the app works on Android N developer preview. 231*90c8c64dSAndroid Build Coastguard Worker * 232*90c8c64dSAndroid Build Coastguard Worker */ createKey(String keyName, boolean invalidatedByBiometricEnrollment)233*90c8c64dSAndroid Build Coastguard Worker public void createKey(String keyName, boolean invalidatedByBiometricEnrollment) { 234*90c8c64dSAndroid Build Coastguard Worker // The enrolling flow for fingerprint. This is where you ask the user to set up fingerprint 235*90c8c64dSAndroid Build Coastguard Worker // for your flow. Use of keys is necessary if you need to know if the set of 236*90c8c64dSAndroid Build Coastguard Worker // enrolled fingerprints has changed. 237*90c8c64dSAndroid Build Coastguard Worker try { 238*90c8c64dSAndroid Build Coastguard Worker mKeyStore.load(null); 239*90c8c64dSAndroid Build Coastguard Worker // Set the alias of the entry in Android KeyStore where the key will appear 240*90c8c64dSAndroid Build Coastguard Worker // and the constrains (purposes) in the constructor of the Builder 241*90c8c64dSAndroid Build Coastguard Worker 242*90c8c64dSAndroid Build Coastguard Worker KeyGenParameterSpec.Builder builder = new KeyGenParameterSpec.Builder(keyName, 243*90c8c64dSAndroid Build Coastguard Worker KeyProperties.PURPOSE_ENCRYPT | 244*90c8c64dSAndroid Build Coastguard Worker KeyProperties.PURPOSE_DECRYPT) 245*90c8c64dSAndroid Build Coastguard Worker .setBlockModes(KeyProperties.BLOCK_MODE_CBC) 246*90c8c64dSAndroid Build Coastguard Worker // Require the user to authenticate with a fingerprint to authorize every use 247*90c8c64dSAndroid Build Coastguard Worker // of the key 248*90c8c64dSAndroid Build Coastguard Worker .setUserAuthenticationRequired(true) 249*90c8c64dSAndroid Build Coastguard Worker .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7); 250*90c8c64dSAndroid Build Coastguard Worker 251*90c8c64dSAndroid Build Coastguard Worker // This is a workaround to avoid crashes on devices whose API level is < 24 252*90c8c64dSAndroid Build Coastguard Worker // because KeyGenParameterSpec.Builder#setInvalidatedByBiometricEnrollment is only 253*90c8c64dSAndroid Build Coastguard Worker // visible on API level +24. 254*90c8c64dSAndroid Build Coastguard Worker // Ideally there should be a compat library for KeyGenParameterSpec.Builder but 255*90c8c64dSAndroid Build Coastguard Worker // which isn't available yet. 256*90c8c64dSAndroid Build Coastguard Worker if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 257*90c8c64dSAndroid Build Coastguard Worker builder.setInvalidatedByBiometricEnrollment(invalidatedByBiometricEnrollment); 258*90c8c64dSAndroid Build Coastguard Worker } 259*90c8c64dSAndroid Build Coastguard Worker mKeyGenerator.init(builder.build()); 260*90c8c64dSAndroid Build Coastguard Worker mKeyGenerator.generateKey(); 261*90c8c64dSAndroid Build Coastguard Worker } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException 262*90c8c64dSAndroid Build Coastguard Worker | CertificateException | IOException e) { 263*90c8c64dSAndroid Build Coastguard Worker throw new RuntimeException(e); 264*90c8c64dSAndroid Build Coastguard Worker } 265*90c8c64dSAndroid Build Coastguard Worker } 266*90c8c64dSAndroid Build Coastguard Worker 267*90c8c64dSAndroid Build Coastguard Worker @Override onCreateOptionsMenu(Menu menu)268*90c8c64dSAndroid Build Coastguard Worker public boolean onCreateOptionsMenu(Menu menu) { 269*90c8c64dSAndroid Build Coastguard Worker getMenuInflater().inflate(R.menu.menu_main, menu); 270*90c8c64dSAndroid Build Coastguard Worker return true; 271*90c8c64dSAndroid Build Coastguard Worker } 272*90c8c64dSAndroid Build Coastguard Worker 273*90c8c64dSAndroid Build Coastguard Worker @Override onOptionsItemSelected(MenuItem item)274*90c8c64dSAndroid Build Coastguard Worker public boolean onOptionsItemSelected(MenuItem item) { 275*90c8c64dSAndroid Build Coastguard Worker int id = item.getItemId(); 276*90c8c64dSAndroid Build Coastguard Worker 277*90c8c64dSAndroid Build Coastguard Worker if (id == R.id.action_settings) { 278*90c8c64dSAndroid Build Coastguard Worker Intent intent = new Intent(this, SettingsActivity.class); 279*90c8c64dSAndroid Build Coastguard Worker startActivity(intent); 280*90c8c64dSAndroid Build Coastguard Worker return true; 281*90c8c64dSAndroid Build Coastguard Worker } 282*90c8c64dSAndroid Build Coastguard Worker return super.onOptionsItemSelected(item); 283*90c8c64dSAndroid Build Coastguard Worker } 284*90c8c64dSAndroid Build Coastguard Worker 285*90c8c64dSAndroid Build Coastguard Worker private class PurchaseButtonClickListener implements View.OnClickListener { 286*90c8c64dSAndroid Build Coastguard Worker 287*90c8c64dSAndroid Build Coastguard Worker Cipher mCipher; 288*90c8c64dSAndroid Build Coastguard Worker String mKeyName; 289*90c8c64dSAndroid Build Coastguard Worker PurchaseButtonClickListener(Cipher cipher, String keyName)290*90c8c64dSAndroid Build Coastguard Worker PurchaseButtonClickListener(Cipher cipher, String keyName) { 291*90c8c64dSAndroid Build Coastguard Worker mCipher = cipher; 292*90c8c64dSAndroid Build Coastguard Worker mKeyName = keyName; 293*90c8c64dSAndroid Build Coastguard Worker } 294*90c8c64dSAndroid Build Coastguard Worker 295*90c8c64dSAndroid Build Coastguard Worker @Override onClick(View view)296*90c8c64dSAndroid Build Coastguard Worker public void onClick(View view) { 297*90c8c64dSAndroid Build Coastguard Worker findViewById(R.id.confirmation_message).setVisibility(View.GONE); 298*90c8c64dSAndroid Build Coastguard Worker findViewById(R.id.encrypted_message).setVisibility(View.GONE); 299*90c8c64dSAndroid Build Coastguard Worker 300*90c8c64dSAndroid Build Coastguard Worker // Set up the crypto object for later. The object will be authenticated by use 301*90c8c64dSAndroid Build Coastguard Worker // of the fingerprint. 302*90c8c64dSAndroid Build Coastguard Worker if (initCipher(mCipher, mKeyName)) { 303*90c8c64dSAndroid Build Coastguard Worker 304*90c8c64dSAndroid Build Coastguard Worker // Show the fingerprint dialog. The user has the option to use the fingerprint with 305*90c8c64dSAndroid Build Coastguard Worker // crypto, or you can fall back to using a server-side verified password. 306*90c8c64dSAndroid Build Coastguard Worker FingerprintAuthenticationDialogFragment fragment 307*90c8c64dSAndroid Build Coastguard Worker = new FingerprintAuthenticationDialogFragment(); 308*90c8c64dSAndroid Build Coastguard Worker fragment.setCryptoObject(new FingerprintManager.CryptoObject(mCipher)); 309*90c8c64dSAndroid Build Coastguard Worker boolean useFingerprintPreference = mSharedPreferences 310*90c8c64dSAndroid Build Coastguard Worker .getBoolean(getString(R.string.use_fingerprint_to_authenticate_key), 311*90c8c64dSAndroid Build Coastguard Worker true); 312*90c8c64dSAndroid Build Coastguard Worker if (useFingerprintPreference) { 313*90c8c64dSAndroid Build Coastguard Worker fragment.setStage( 314*90c8c64dSAndroid Build Coastguard Worker FingerprintAuthenticationDialogFragment.Stage.FINGERPRINT); 315*90c8c64dSAndroid Build Coastguard Worker } else { 316*90c8c64dSAndroid Build Coastguard Worker fragment.setStage( 317*90c8c64dSAndroid Build Coastguard Worker FingerprintAuthenticationDialogFragment.Stage.PASSWORD); 318*90c8c64dSAndroid Build Coastguard Worker } 319*90c8c64dSAndroid Build Coastguard Worker fragment.show(getFragmentManager(), DIALOG_FRAGMENT_TAG); 320*90c8c64dSAndroid Build Coastguard Worker } else { 321*90c8c64dSAndroid Build Coastguard Worker // This happens if the lock screen has been disabled or or a fingerprint got 322*90c8c64dSAndroid Build Coastguard Worker // enrolled. Thus show the dialog to authenticate with their password first 323*90c8c64dSAndroid Build Coastguard Worker // and ask the user if they want to authenticate with fingerprints in the 324*90c8c64dSAndroid Build Coastguard Worker // future 325*90c8c64dSAndroid Build Coastguard Worker FingerprintAuthenticationDialogFragment fragment 326*90c8c64dSAndroid Build Coastguard Worker = new FingerprintAuthenticationDialogFragment(); 327*90c8c64dSAndroid Build Coastguard Worker fragment.setCryptoObject(new FingerprintManager.CryptoObject(mCipher)); 328*90c8c64dSAndroid Build Coastguard Worker fragment.setStage( 329*90c8c64dSAndroid Build Coastguard Worker FingerprintAuthenticationDialogFragment.Stage.NEW_FINGERPRINT_ENROLLED); 330*90c8c64dSAndroid Build Coastguard Worker fragment.show(getFragmentManager(), DIALOG_FRAGMENT_TAG); 331*90c8c64dSAndroid Build Coastguard Worker } 332*90c8c64dSAndroid Build Coastguard Worker } 333*90c8c64dSAndroid Build Coastguard Worker } 334*90c8c64dSAndroid Build Coastguard Worker } 335