Files
android_development/samples/browseable/ConfirmCredential/src/com.example.android.confirmcredential/MainActivity.java
Trevor Johns d95a687e5a Sync sample prebuilts for mnc-dev
Synced to //developers/samples/android commit 89d2da0f4b.

Change-Id: I904da8210517922531d1ac1ba1e747f1c7bf00b3
2015-08-13 21:10:42 -07:00

204 lines
8.7 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.example.android.confirmcredential;
import android.app.Activity;
import android.app.KeyguardManager;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore.KeyProperties;
import android.security.keystore.UserNotAuthenticatedException;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
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;
/**
* Main entry point for the sample, showing a backpack and "Purchase" button.
*/
public class MainActivity extends Activity {
/** Alias for our key in the Android Key Store. */
private static final String KEY_NAME = "my_key";
private static final byte[] SECRET_BYTE_ARRAY = new byte[] {1, 2, 3, 4, 5, 6};
private static final int REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS = 1;
/**
* If the user has unlocked the device Within the last this number of seconds,
* it can be considered as an authenticator.
*/
private static final int AUTHENTICATION_DURATION_SECONDS = 30;
private KeyguardManager mKeyguardManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mKeyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
Button purchaseButton = (Button) findViewById(R.id.purchase_button);
if (!mKeyguardManager.isKeyguardSecure()) {
// Show a message that the user hasn't set up a lock screen.
Toast.makeText(this,
"Secure lock screen hasn't set up.\n"
+ "Go to 'Settings -> Security -> Screenlock' to set up a lock screen",
Toast.LENGTH_LONG).show();
purchaseButton.setEnabled(false);
return;
}
createKey();
findViewById(R.id.purchase_button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Test to encrypt something. It might fail if the timeout expired (30s).
tryEncrypt();
}
});
}
/**
* Tries to encrypt some data with the generated key in {@link #createKey} which is
* only works if the user has just authenticated via device credentials.
*/
private boolean tryEncrypt() {
try {
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
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.
showAlreadyAuthenticated();
return true;
} catch (UserNotAuthenticatedException e) {
// User is not authenticated, let's authenticate with device credentials.
showAuthenticationScreen();
return false;
} catch (KeyPermanentlyInvalidatedException e) {
// This happens if the lock screen has been disabled or reset after the key was
// generated after the key was generated.
Toast.makeText(this, "Keys are invalidated after created. Retry the purchase\n"
+ e.getMessage(),
Toast.LENGTH_LONG).show();
return false;
} catch (BadPaddingException | IllegalBlockSizeException | KeyStoreException |
CertificateException | UnrecoverableKeyException | IOException
| NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException e) {
throw new RuntimeException(e);
}
}
/**
* 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.
*/
private void createKey() {
// Generate a key to decrypt payment credentials, tokens, etc.
// This will most likely be a registration step for the user when they are setting up your app.
try {
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
KeyGenerator keyGenerator = KeyGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
// 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) {
throw new RuntimeException("Failed to create a symmetric key", e);
}
}
private void showAuthenticationScreen() {
// Create the Confirm Credentials screen. You can customize the title and description. Or
// we will provide a generic one for you if you leave it null
Intent intent = mKeyguardManager.createConfirmDeviceCredentialIntent(null, null);
if (intent != null) {
startActivityForResult(intent, REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS) {
// Challenge completed, proceed with using cipher
if (resultCode == RESULT_OK) {
if (tryEncrypt()) {
showPurchaseConfirmation();
}
} else {
// The user canceled or didnt complete the lock screen
// operation. Go to error/cancellation flow.
}
}
}
private void showPurchaseConfirmation() {
findViewById(R.id.confirmation_message).setVisibility(View.VISIBLE);
findViewById(R.id.purchase_button).setEnabled(false);
}
private void showAlreadyAuthenticated() {
TextView textView = (TextView) findViewById(
R.id.already_has_valid_device_credential_message);
textView.setVisibility(View.VISIBLE);
textView.setText(getString(
R.string.already_confirmed_device_credentials_within_last_x_seconds,
AUTHENTICATION_DURATION_SECONDS));
findViewById(R.id.purchase_button).setEnabled(false);
}
}