aboutsummaryrefslogtreecommitdiff
path: root/security/FingerprintDialog
diff options
context:
space:
mode:
authorYuichi Araki <yaraki@google.com>2015-09-15 01:56:26 +0000
committerYuichi Araki <yaraki@google.com>2015-09-15 01:56:26 +0000
commitf1785a1c0f454c3c754a9a41ae06eac344af8daf (patch)
tree281154f342ac4b9fe8ce22098d7623a695a5e741 /security/FingerprintDialog
parent69a36a78ca293b94452c69d06c638de804815c40 (diff)
downloadandroid-f1785a1c0f454c3c754a9a41ae06eac344af8daf.tar.gz
Revert "FingerprintDialog: Use asymmetric keys"
This reverts commit 69a36a78ca293b94452c69d06c638de804815c40. Change-Id: I50741f5f028cb16cab6fbbebe3918ae77ce93abe
Diffstat (limited to 'security/FingerprintDialog')
-rw-r--r--security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/FingerprintAuthenticationDialogFragment.java83
-rw-r--r--security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/FingerprintModule.java30
-rw-r--r--security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/MainActivity.java93
-rw-r--r--security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/server/StoreBackend.java61
-rw-r--r--security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/server/StoreBackendImpl.java69
-rw-r--r--security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/server/Transaction.java66
-rw-r--r--security/FingerprintDialog/Application/src/main/res/values/strings.xml1
7 files changed, 87 insertions, 316 deletions
diff --git a/security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/FingerprintAuthenticationDialogFragment.java b/security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/FingerprintAuthenticationDialogFragment.java
index 17df7217..b17ebb0b 100644
--- a/security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/FingerprintAuthenticationDialogFragment.java
+++ b/security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/FingerprintAuthenticationDialogFragment.java
@@ -16,9 +16,6 @@
package com.example.android.fingerprintdialog;
-import com.example.android.fingerprintdialog.server.StoreBackend;
-import com.example.android.fingerprintdialog.server.Transaction;
-
import android.app.Activity;
import android.app.DialogFragment;
import android.content.SharedPreferences;
@@ -36,18 +33,6 @@ import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
-import java.io.IOException;
-import java.security.KeyFactory;
-import java.security.KeyStore;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
-import java.security.PublicKey;
-import java.security.Signature;
-import java.security.SignatureException;
-import java.security.cert.CertificateException;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.X509EncodedKeySpec;
-
import javax.inject.Inject;
/**
@@ -75,7 +60,6 @@ public class FingerprintAuthenticationDialogFragment extends DialogFragment
@Inject FingerprintUiHelper.FingerprintUiHelperBuilder mFingerprintUiHelperBuilder;
@Inject InputMethodManager mInputMethodManager;
@Inject SharedPreferences mSharedPreferences;
- @Inject StoreBackend mStoreBackend;
@Inject
public FingerprintAuthenticationDialogFragment() {}
@@ -87,9 +71,6 @@ public class FingerprintAuthenticationDialogFragment extends DialogFragment
// Do not create a new Fragment when the Activity is re-created such as orientation changes.
setRetainInstance(true);
setStyle(DialogFragment.STYLE_NORMAL, android.R.style.Theme_Material_Light_Dialog);
-
- // We register a new user account here. Real apps should do this with proper UIs.
- enroll();
}
@Override
@@ -187,38 +168,11 @@ public class FingerprintAuthenticationDialogFragment extends DialogFragment
}
/**
- * Enrolls a user to the fake backend.
- */
- private void enroll() {
- try {
- KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
- keyStore.load(null);
- PublicKey publicKey = keyStore.getCertificate(MainActivity.KEY_NAME).getPublicKey();
- // Provide the public key to the backend. In most cases, the key needs to be transmitted
- // to the backend over the network, for which Key.getEncoded provides a suitable wire
- // format (X.509 DER-encoded). The backend can then create a PublicKey instance from the
- // X.509 encoded form using KeyFactory.generatePublic. This conversion is also currently
- // needed on API Level 23 (Android M) due to a platform bug which prevents the use of
- // Android Keystore public keys when their private keys require user authentication.
- // This conversion creates a new public key which is not backed by Android Keystore and
- // thus does not is not affected by the bug.
- KeyFactory factory = KeyFactory.getInstance(publicKey.getAlgorithm());
- X509EncodedKeySpec spec = new X509EncodedKeySpec(publicKey.getEncoded());
- PublicKey verificationKey = factory.generatePublic(spec);
- mStoreBackend.enroll("user", "password", verificationKey);
- } catch (KeyStoreException | CertificateException | NoSuchAlgorithmException |
- IOException | InvalidKeySpecException e) {
- e.printStackTrace();
- }
- }
-
- /**
- * Checks whether the current entered password is correct, and dismisses the the dialog and lets
- * the activity know about the result.
+ * Checks whether the current entered password is correct, and dismisses the the dialog and
+ * let's the activity know about the result.
*/
private void verifyPassword() {
- Transaction transaction = new Transaction("user", 1);
- if (!mStoreBackend.verify(transaction, mPassword.getText().toString())) {
+ if (!checkPassword(mPassword.getText().toString())) {
return;
}
if (mStage == Stage.NEW_FINGERPRINT_ENROLLED) {
@@ -229,15 +183,24 @@ public class FingerprintAuthenticationDialogFragment extends DialogFragment
if (mUseFingerprintFutureCheckBox.isChecked()) {
// Re-create the key so that fingerprints including new ones are validated.
- mActivity.createKeyPair();
+ mActivity.createKey();
mStage = Stage.FINGERPRINT;
}
}
mPassword.setText("");
- mActivity.onPurchased(null);
+ mActivity.onPurchased(false /* without Fingerprint */);
dismiss();
}
+ /**
+ * @return true if {@code password} is correct, false otherwise
+ */
+ private boolean checkPassword(String password) {
+ // Assume the password is always correct.
+ // In the real world situation, the password needs to be verified in the server side.
+ return password.length() > 0;
+ }
+
private final Runnable mShowKeyboardRunnable = new Runnable() {
@Override
public void run() {
@@ -282,22 +245,8 @@ public class FingerprintAuthenticationDialogFragment extends DialogFragment
public void onAuthenticated() {
// Callback from FingerprintUiHelper. Let the activity know that authentication was
// successful.
- mPassword.setText("");
- Signature signature = mCryptoObject.getSignature();
- Transaction transaction = new Transaction("user", 1);
- try {
- signature.update(transaction.toByteArray());
- byte[] sigBytes = signature.sign();
- if (mStoreBackend.verify(transaction, sigBytes)) {
- mActivity.onPurchased(sigBytes);
- dismiss();
- } else {
- mActivity.onPurchaseFailed();
- dismiss();
- }
- } catch (SignatureException e) {
- throw new RuntimeException(e);
- }
+ mActivity.onPurchased(true /* withFingerprint */);
+ dismiss();
}
@Override
diff --git a/security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/FingerprintModule.java b/security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/FingerprintModule.java
index ddfe35c3..964e1f6d 100644
--- a/security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/FingerprintModule.java
+++ b/security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/FingerprintModule.java
@@ -16,9 +16,6 @@
package com.example.android.fingerprintdialog;
-import com.example.android.fingerprintdialog.server.StoreBackend;
-import com.example.android.fingerprintdialog.server.StoreBackendImpl;
-
import android.app.KeyguardManager;
import android.content.Context;
import android.content.SharedPreferences;
@@ -27,12 +24,14 @@ import android.preference.PreferenceManager;
import android.security.keystore.KeyProperties;
import android.view.inputmethod.InputMethodManager;
-import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
-import java.security.Signature;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyGenerator;
+import javax.crypto.NoSuchPaddingException;
import dagger.Module;
import dagger.Provides;
@@ -77,20 +76,22 @@ public class FingerprintModule {
}
@Provides
- public KeyPairGenerator providesKeyPairGenerator() {
+ public KeyGenerator providesKeyGenerator() {
try {
- return KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore");
+ return KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
} catch (NoSuchAlgorithmException | NoSuchProviderException e) {
- throw new RuntimeException("Failed to get an instance of KeyPairGenerator", e);
+ throw new RuntimeException("Failed to get an instance of KeyGenerator", e);
}
}
@Provides
- public Signature providesSignature(KeyStore keyStore) {
+ public Cipher providesCipher(KeyStore keyStore) {
try {
- return Signature.getInstance("SHA256withECDSA");
- } catch (NoSuchAlgorithmException e) {
- throw new RuntimeException("Failed to get an instance of Signature", e);
+ return Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
+ + KeyProperties.BLOCK_MODE_CBC + "/"
+ + KeyProperties.ENCRYPTION_PADDING_PKCS7);
+ } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
+ throw new RuntimeException("Failed to get an instance of Cipher", e);
}
}
@@ -103,9 +104,4 @@ public class FingerprintModule {
public SharedPreferences providesSharedPreferences(Context context) {
return PreferenceManager.getDefaultSharedPreferences(context);
}
-
- @Provides
- public StoreBackend providesStoreBackend() {
- return new StoreBackendImpl();
- }
}
diff --git a/security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/MainActivity.java b/security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/MainActivity.java
index caccd823..c954bfa7 100644
--- a/security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/MainActivity.java
+++ b/security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/MainActivity.java
@@ -28,6 +28,7 @@ import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore.KeyProperties;
import android.util.Base64;
+import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
@@ -38,16 +39,17 @@ import android.widget.Toast;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
-import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
-import java.security.Signature;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
-import java.security.spec.ECGenParameterSpec;
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
import javax.inject.Inject;
/**
@@ -60,7 +62,7 @@ public class MainActivity extends Activity {
private static final String DIALOG_FRAGMENT_TAG = "myFragment";
private static final String SECRET_MESSAGE = "Very secret message";
/** Alias for our key in the Android Key Store */
- public static final String KEY_NAME = "my_key";
+ private static final String KEY_NAME = "my_key";
private static final int FINGERPRINT_PERMISSION_REQUEST_CODE = 0;
@@ -68,8 +70,8 @@ public class MainActivity extends Activity {
@Inject FingerprintManager mFingerprintManager;
@Inject FingerprintAuthenticationDialogFragment mFragment;
@Inject KeyStore mKeyStore;
- @Inject KeyPairGenerator mKeyPairGenerator;
- @Inject Signature mSignature;
+ @Inject KeyGenerator mKeyGenerator;
+ @Inject Cipher mCipher;
@Inject SharedPreferences mSharedPreferences;
@Override
@@ -104,7 +106,7 @@ public class MainActivity extends Activity {
Toast.LENGTH_LONG).show();
return;
}
- createKeyPair();
+ createKey();
purchaseButton.setEnabled(true);
purchaseButton.setOnClickListener(new View.OnClickListener() {
@Override
@@ -114,11 +116,11 @@ public class MainActivity extends Activity {
// Set up the crypto object for later. The object will be authenticated by use
// of the fingerprint.
- if (initSignature()) {
+ if (initCipher()) {
// Show the fingerprint dialog. The user has the option to use the fingerprint with
// crypto, or you can fall back to using a server-side verified password.
- mFragment.setCryptoObject(new FingerprintManager.CryptoObject(mSignature));
+ mFragment.setCryptoObject(new FingerprintManager.CryptoObject(mCipher));
boolean useFingerprintPreference = mSharedPreferences
.getBoolean(getString(R.string.use_fingerprint_to_authenticate_key),
true);
@@ -135,6 +137,7 @@ public class MainActivity extends Activity {
// enrolled. Thus show the dialog to authenticate with their password first
// and ask the user if they want to authenticate with fingerprints in the
// future
+ mFragment.setCryptoObject(new FingerprintManager.CryptoObject(mCipher));
mFragment.setStage(
FingerprintAuthenticationDialogFragment.Stage.NEW_FINGERPRINT_ENROLLED);
mFragment.show(getFragmentManager(), DIALOG_FRAGMENT_TAG);
@@ -145,18 +148,18 @@ public class MainActivity extends Activity {
}
/**
- * Initialize the {@link Signature} instance with the created key in the
- * {@link #createKeyPair()} method.
+ * Initialize the {@link Cipher} instance with the created key in the {@link #createKey()}
+ * method.
*
* @return {@code true} if initialization is successful, {@code false} if the lock screen has
* been disabled or reset after the key was generated, or if a fingerprint got enrolled after
* the key was generated.
*/
- private boolean initSignature() {
+ private boolean initCipher() {
try {
mKeyStore.load(null);
- PrivateKey key = (PrivateKey) mKeyStore.getKey(KEY_NAME, null);
- mSignature.initSign(key);
+ SecretKey key = (SecretKey) mKeyStore.getKey(KEY_NAME, null);
+ mCipher.init(Cipher.ENCRYPT_MODE, key);
return true;
} catch (KeyPermanentlyInvalidatedException e) {
return false;
@@ -166,12 +169,15 @@ public class MainActivity extends Activity {
}
}
- public void onPurchased(byte[] signature) {
- showConfirmation(signature);
- }
-
- public void onPurchaseFailed() {
- Toast.makeText(this, R.string.purchase_fail, Toast.LENGTH_SHORT).show();
+ public void onPurchased(boolean withFingerprint) {
+ if (withFingerprint) {
+ // If the user has authenticated with fingerprint, verify that using cryptography and
+ // then show the confirmation message.
+ tryEncrypt();
+ } else {
+ // Authentication happened with backup password. Just show the confirmation message.
+ showConfirmation(null);
+ }
}
// Show confirmation, if fingerprint was used show crypto information.
@@ -185,27 +191,44 @@ public class MainActivity extends Activity {
}
/**
- * Generates an asymmetric key pair in the Android Keystore. Every use of the private key must
- * be authorized by the user authenticating with fingerprint. Public key use is unrestricted.
+ * Tries to encrypt some data with the generated key in {@link #createKey} which is
+ * only works if the user has just authenticated via fingerprint.
+ */
+ private void tryEncrypt() {
+ try {
+ byte[] encrypted = mCipher.doFinal(SECRET_MESSAGE.getBytes());
+ showConfirmation(encrypted);
+ } catch (BadPaddingException | IllegalBlockSizeException e) {
+ Toast.makeText(this, "Failed to encrypt the data with the generated key. "
+ + "Retry the purchase", Toast.LENGTH_LONG).show();
+ Log.e(TAG, "Failed to encrypt the data with the generated key." + e.getMessage());
+ }
+ }
+
+ /**
+ * Creates a symmetric key in the Android Key Store which can only be used after the user has
+ * authenticated with fingerprint.
*/
- public void createKeyPair() {
+ public void createKey() {
// The enrolling flow for fingerprint. This is where you ask the user to set up fingerprint
// for your flow. Use of keys is necessary if you need to know if the set of
// enrolled fingerprints has changed.
try {
+ mKeyStore.load(null);
// Set the alias of the entry in Android KeyStore where the key will appear
// and the constrains (purposes) in the constructor of the Builder
- mKeyPairGenerator.initialize(
- new KeyGenParameterSpec.Builder(KEY_NAME,
- KeyProperties.PURPOSE_SIGN)
- .setDigests(KeyProperties.DIGEST_SHA256)
- .setAlgorithmParameterSpec(new ECGenParameterSpec("secp256r1"))
- // Require the user to authenticate with a fingerprint to authorize
- // every use of the private key
- .setUserAuthenticationRequired(true)
- .build());
- mKeyPairGenerator.generateKeyPair();
- } catch (InvalidAlgorithmParameterException e) {
+ mKeyGenerator.init(new KeyGenParameterSpec.Builder(KEY_NAME,
+ KeyProperties.PURPOSE_ENCRYPT |
+ KeyProperties.PURPOSE_DECRYPT)
+ .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
+ // Require the user to authenticate with a fingerprint to authorize every use
+ // of the key
+ .setUserAuthenticationRequired(true)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
+ .build());
+ mKeyGenerator.generateKey();
+ } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException
+ | CertificateException | IOException e) {
throw new RuntimeException(e);
}
}
diff --git a/security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/server/StoreBackend.java b/security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/server/StoreBackend.java
deleted file mode 100644
index 5ecfa9e7..00000000
--- a/security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/server/StoreBackend.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * 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.fingerprintdialog.server;
-
-import java.security.PublicKey;
-
-/**
- * An interface that defines the methods required for the store backend.
- */
-public interface StoreBackend {
-
- /**
- * Verifies the authenticity of the provided transaction by confirming that it was signed with
- * the private key enrolled for the userId.
- *
- * @param transaction the contents of the purchase transaction, its contents are
- * signed
- * by the
- * private key in the client side.
- * @param transactionSignature the signature of the transaction's contents.
- * @return true if the signedSignature was verified, false otherwise. If this method returns
- * true, the server can consider the transaction is successful.
- */
- boolean verify(Transaction transaction, byte[] transactionSignature);
-
- /**
- * Verifies the authenticity of the provided transaction by password.
- *
- * @param transaction the contents of the purchase transaction, its contents are signed by the
- * private key in the client side.
- * @param password the password for the user associated with the {@code transaction}.
- * @return true if the password is verified.
- */
- boolean verify(Transaction transaction, String password);
-
- /**
- * Enrolls a public key associated with the userId
- *
- * @param userId the unique ID of the user within the app including server side
- * implementation
- * @param password the password for the user for the server side
- * @param publicKey the public key object to verify the signature from the user
- * @return true if the enrollment was successful, false otherwise
- */
- boolean enroll(String userId, String password, PublicKey publicKey);
-
-}
diff --git a/security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/server/StoreBackendImpl.java b/security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/server/StoreBackendImpl.java
deleted file mode 100644
index 986479e0..00000000
--- a/security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/server/StoreBackendImpl.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * 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.fingerprintdialog.server;
-
-
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.security.PublicKey;
-import java.security.Signature;
-import java.security.SignatureException;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * A fake backend implementation of {@link StoreBackend}.
- */
-public class StoreBackendImpl implements StoreBackend {
-
- private final Map<String, PublicKey> mPublicKeys = new HashMap<>();
-
- @Override
- public boolean verify(Transaction transaction, byte[] transactionSignature) {
- try {
- PublicKey publicKey = mPublicKeys.get(transaction.getUserId());
- Signature verificationFunction = Signature.getInstance("SHA256withECDSA");
- verificationFunction.initVerify(publicKey);
- verificationFunction.update(transaction.toByteArray());
- if (verificationFunction.verify(transactionSignature)) {
- // Transaction is verified with the public key associated with the user
- // Do some post purchase processing in the server
- return true;
- }
- } catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) {
- // In a real world, better to send some error message to the user
- }
- return false;
- }
-
- @Override
- public boolean verify(Transaction transaction, String password) {
- // As this is just a sample, we always assume that the password is right.
- return true;
- }
-
- @Override
- public boolean enroll(String userId, String password, PublicKey publicKey) {
- if (publicKey != null) {
- mPublicKeys.put(userId, publicKey);
- }
- // We just ignore the provided password here, but in real life, it is registered to the
- // backend.
- return true;
- }
-
-}
diff --git a/security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/server/Transaction.java b/security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/server/Transaction.java
deleted file mode 100644
index 724102ec..00000000
--- a/security/FingerprintDialog/Application/src/main/java/com/example/android/fingerprintdialog/server/Transaction.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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.fingerprintdialog.server;
-
-import java.io.ByteArrayOutputStream;
-import java.io.DataOutputStream;
-import java.io.IOException;
-
-/**
- * An entity that represents a single transaction (purchase) of an item.
- */
-public class Transaction {
-
- /** The unique ID of the item of the purchase */
- private final Long mItemId;
-
- /** The unique user ID who made the transaction */
- private final String mUserId;
-
- public Transaction(String userId, long itemId) {
- mItemId = itemId;
- mUserId = userId;
- }
-
- public String getUserId() {
- return mUserId;
- }
-
- public byte[] toByteArray() {
- ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
- DataOutputStream dataOutputStream = null;
- try {
- dataOutputStream = new DataOutputStream(byteArrayOutputStream);
- dataOutputStream.writeLong(mItemId);
- dataOutputStream.writeUTF(mUserId);
- return byteArrayOutputStream.toByteArray();
- } catch (IOException e) {
- throw new RuntimeException(e);
- } finally {
- try {
- if (dataOutputStream != null) {
- dataOutputStream.close();
- }
- } catch (IOException ignore) {
- }
- try {
- byteArrayOutputStream.close();
- } catch (IOException ignore) {
- }
- }
- }
-}
diff --git a/security/FingerprintDialog/Application/src/main/res/values/strings.xml b/security/FingerprintDialog/Application/src/main/res/values/strings.xml
index f44c06d6..9f5a6fd1 100644
--- a/security/FingerprintDialog/Application/src/main/res/values/strings.xml
+++ b/security/FingerprintDialog/Application/src/main/res/values/strings.xml
@@ -31,7 +31,6 @@
<string name="item_price">$62.68</string>
<string name="item_description">Mesh backpack in white. Black textile trim throughout.</string>
<string name="purchase_done">Purchase successful</string>
- <string name="purchase_fail">Purchase failed</string>
<string name="new_fingerprint_enrolled_description">A new fingerprint was added to this device, so your password is required.</string>
<string name="use_fingerprint_in_future">Use fingerprint in the future</string>
<string name="use_fingerprint_to_authenticate_title">Use fingerprint to authenticate</string>