From 54c876b6980f9e67a4acc50b91ad06296f6798f6 Mon Sep 17 00:00:00 2001 From: Takeshi Hagikura Date: Mon, 5 Oct 2015 15:43:12 +0900 Subject: Add a client nonce in the transactions to prevent replay attacks. Change-Id: Icf98c918223fc4b738c569f764726032329cffee --- .../FingerprintAuthenticationDialogFragment.java | 8 ++++-- .../asymmetricfingerprintdialog/MainActivity.java | 3 --- .../server/StoreBackend.java | 1 - .../server/StoreBackendImpl.java | 10 +++++++- .../server/Transaction.java | 30 +++++++++++++++++++++- 5 files changed, 44 insertions(+), 8 deletions(-) (limited to 'security/AsymmetricFingerprintDialog') diff --git a/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/FingerprintAuthenticationDialogFragment.java b/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/FingerprintAuthenticationDialogFragment.java index d40661c6..a56556f1 100644 --- a/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/FingerprintAuthenticationDialogFragment.java +++ b/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/FingerprintAuthenticationDialogFragment.java @@ -42,6 +42,7 @@ import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; +import java.security.SecureRandom; import java.security.Signature; import java.security.SignatureException; import java.security.cert.CertificateException; @@ -217,7 +218,7 @@ public class FingerprintAuthenticationDialogFragment extends DialogFragment * the activity know about the result. */ private void verifyPassword() { - Transaction transaction = new Transaction("user", 1); + Transaction transaction = new Transaction("user", 1, new SecureRandom().nextLong()); if (!mStoreBackend.verify(transaction, mPassword.getText().toString())) { return; } @@ -284,7 +285,10 @@ public class FingerprintAuthenticationDialogFragment extends DialogFragment // successful. mPassword.setText(""); Signature signature = mCryptoObject.getSignature(); - Transaction transaction = new Transaction("user", 1); + // Include a client nonce in the transaction so that the nonce is also signed by the private + // key and the backend can verify that the same nonce can't be used to prevent replay + // attacks. + Transaction transaction = new Transaction("user", 1, new SecureRandom().nextLong()); try { signature.update(transaction.toByteArray()); byte[] sigBytes = signature.sign(); diff --git a/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/MainActivity.java b/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/MainActivity.java index e6031c52..5086a173 100644 --- a/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/MainActivity.java +++ b/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/MainActivity.java @@ -55,10 +55,7 @@ import javax.inject.Inject; */ public class MainActivity extends Activity { - private static final String TAG = MainActivity.class.getSimpleName(); - 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"; diff --git a/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/server/StoreBackend.java b/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/server/StoreBackend.java index dde81519..87921ae5 100644 --- a/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/server/StoreBackend.java +++ b/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/server/StoreBackend.java @@ -57,5 +57,4 @@ public interface StoreBackend { * @return true if the enrollment was successful, false otherwise */ boolean enroll(String userId, String password, PublicKey publicKey); - } diff --git a/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/server/StoreBackendImpl.java b/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/server/StoreBackendImpl.java index 8bf48d89..b28dce49 100644 --- a/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/server/StoreBackendImpl.java +++ b/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/server/StoreBackendImpl.java @@ -23,7 +23,9 @@ import java.security.PublicKey; import java.security.Signature; import java.security.SignatureException; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; /** * A fake backend implementation of {@link StoreBackend}. @@ -31,10 +33,17 @@ import java.util.Map; public class StoreBackendImpl implements StoreBackend { private final Map mPublicKeys = new HashMap<>(); + private final Set mReceivedTransactions = new HashSet<>(); @Override public boolean verify(Transaction transaction, byte[] transactionSignature) { try { + if (mReceivedTransactions.contains(transaction)) { + // It verifies the equality of the transaction including the client nonce + // So attackers can't do replay attacks. + return false; + } + mReceivedTransactions.add(transaction); PublicKey publicKey = mPublicKeys.get(transaction.getUserId()); Signature verificationFunction = Signature.getInstance("SHA256withECDSA"); verificationFunction.initVerify(publicKey); @@ -65,5 +74,4 @@ public class StoreBackendImpl implements StoreBackend { // backend. return true; } - } diff --git a/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/server/Transaction.java b/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/server/Transaction.java index 33b65ef8..789cc0e7 100644 --- a/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/server/Transaction.java +++ b/security/AsymmetricFingerprintDialog/Application/src/main/java/com/example/android/asymmetricfingerprintdialog/server/Transaction.java @@ -19,6 +19,7 @@ package com.example.android.asymmetricfingerprintdialog.server; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; +import java.util.Objects; /** * An entity that represents a single transaction (purchase) of an item. @@ -31,9 +32,16 @@ public class Transaction { /** The unique user ID who made the transaction */ private final String mUserId; - public Transaction(String userId, long itemId) { + /** + * The random long value that will be also signed by the private key and verified in the server + * that the same nonce can't be reused to prevent replay attacks. + */ + private final Long mClientNonce; + + public Transaction(String userId, long itemId, long clientNonce) { mItemId = itemId; mUserId = userId; + mClientNonce = clientNonce; } public String getUserId() { @@ -47,6 +55,7 @@ public class Transaction { dataOutputStream = new DataOutputStream(byteArrayOutputStream); dataOutputStream.writeLong(mItemId); dataOutputStream.writeUTF(mUserId); + dataOutputStream.writeLong(mClientNonce); return byteArrayOutputStream.toByteArray(); } catch (IOException e) { throw new RuntimeException(e); @@ -63,4 +72,23 @@ public class Transaction { } } } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + Transaction that = (Transaction) o; + return Objects.equals(mItemId, that.mItemId) && Objects.equals(mUserId, that.mUserId) && + Objects.equals(mClientNonce, that.mClientNonce); + } + + @Override + public int hashCode() { + return Objects.hash(mItemId, mUserId, mClientNonce); + } } -- cgit v1.2.3