diff --git a/app/src/main/java/org/thoughtcrime/securesms/PassphrasePromptActivity.java b/app/src/main/java/org/thoughtcrime/securesms/PassphrasePromptActivity.java
index 7cbb0533f0..09f8efafe6 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/PassphrasePromptActivity.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/PassphrasePromptActivity.java
@@ -39,11 +39,14 @@ import android.widget.ImageView;
 import androidx.core.hardware.fingerprint.FingerprintManagerCompat;
 import androidx.core.os.CancellationSignal;
 
-import org.thoughtcrime.securesms.util.AnimationCompleteListener;
-import org.thoughtcrime.securesms.components.AnimatingToggle;
-import org.session.libsignal.utilities.Log;
-import org.thoughtcrime.securesms.service.KeyCachingService;
 import org.session.libsession.utilities.TextSecurePreferences;
+import org.session.libsignal.utilities.Log;
+import org.thoughtcrime.securesms.components.AnimatingToggle;
+import org.thoughtcrime.securesms.crypto.BiometricSecretProvider;
+import org.thoughtcrime.securesms.service.KeyCachingService;
+import org.thoughtcrime.securesms.util.AnimationCompleteListener;
+
+import java.security.Signature;
 
 import network.loki.messenger.R;
 
@@ -61,6 +64,8 @@ public class PassphrasePromptActivity extends BaseActionBarActivity {
   private CancellationSignal       fingerprintCancellationSignal;
   private FingerprintListener      fingerprintListener;
 
+  private final BiometricSecretProvider biometricSecretProvider = new BiometricSecretProvider();
+
   private boolean authenticated;
   private boolean failure;
 
@@ -200,7 +205,7 @@ public class PassphrasePromptActivity extends BaseActionBarActivity {
     if (fingerprintManager.isHardwareDetected() && fingerprintManager.hasEnrolledFingerprints()) {
       Log.i(TAG, "Listening for fingerprints...");
       fingerprintCancellationSignal = new CancellationSignal();
-      fingerprintManager.authenticate(null, 0, fingerprintCancellationSignal, fingerprintListener, null);
+      fingerprintManager.authenticate(new FingerprintManagerCompat.CryptoObject(biometricSecretProvider.getOrCreateBiometricSignature(this)), 0, fingerprintCancellationSignal, fingerprintListener, null);
     } else {
       Log.i(TAG, "firing intent...");
       Intent intent = keyguardManager.createConfirmDeviceCredentialIntent("Unlock Session", "");
@@ -224,6 +229,27 @@ public class PassphrasePromptActivity extends BaseActionBarActivity {
     @Override
     public void onAuthenticationSucceeded(FingerprintManagerCompat.AuthenticationResult result) {
       Log.i(TAG, "onAuthenticationSucceeded");
+      if (result.getCryptoObject() == null || result.getCryptoObject().getSignature() == null) {
+        // authentication failed
+        onAuthenticationFailed();
+        return;
+      }
+      // Signature object now successfully unlocked
+      boolean authenticationSucceeded = false;
+      try {
+        Signature signature = result.getCryptoObject().getSignature();
+        byte[] random = biometricSecretProvider.getRandomData();
+        signature.update(random);
+        byte[] signed = signature.sign();
+        authenticationSucceeded = biometricSecretProvider.verifySignature(random, signed);
+      } catch (Exception e) {
+        Log.e(TAG, "onAuthentication signature generation and verification failed", e);
+      }
+      if (!authenticationSucceeded) {
+        onAuthenticationFailed();
+        return;
+      }
+
       fingerprintPrompt.setImageResource(R.drawable.ic_check_white_48dp);
       fingerprintPrompt.getBackground().setColorFilter(getResources().getColor(R.color.green_500), PorterDuff.Mode.SRC_IN);
       fingerprintPrompt.animate().setInterpolator(new BounceInterpolator()).scaleX(1.1f).scaleY(1.1f).setDuration(500).setListener(new AnimationCompleteListener() {
@@ -239,7 +265,7 @@ public class PassphrasePromptActivity extends BaseActionBarActivity {
 
     @Override
     public void onAuthenticationFailed() {
-      Log.w(TAG, "onAuthenticatoinFailed()");
+      Log.w(TAG, "onAuthenticationFailed()");
 
       fingerprintPrompt.setImageResource(R.drawable.ic_close_white_48dp);
       fingerprintPrompt.getBackground().setColorFilter(getResources().getColor(R.color.red_500), PorterDuff.Mode.SRC_IN);
diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/BiometricSecretProvider.kt b/app/src/main/java/org/thoughtcrime/securesms/crypto/BiometricSecretProvider.kt
new file mode 100644
index 0000000000..fe9c0c7fcf
--- /dev/null
+++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/BiometricSecretProvider.kt
@@ -0,0 +1,75 @@
+package org.thoughtcrime.securesms.crypto
+
+import android.content.Context
+import android.content.pm.PackageManager
+import android.os.Build
+import android.security.keystore.KeyGenParameterSpec
+import android.security.keystore.KeyProperties
+import org.session.libsession.utilities.Util
+import java.security.KeyPairGenerator
+import java.security.KeyStore
+import java.security.PrivateKey
+import java.security.Signature
+
+class BiometricSecretProvider {
+
+    companion object {
+        private const val BIOMETRIC_ASYM_KEY_ALIAS = "Session-biometric-asym"
+        private const val ANDROID_KEYSTORE = "AndroidKeyStore"
+        private const val SIGNATURE_ALGORITHM = "SHA512withECDSA"
+    }
+
+    fun getRandomData() = Util.getSecretBytes(32)
+
+    private fun createAsymmetricKey(context: Context) {
+        val keyGenerator = KeyPairGenerator.getInstance(
+            KeyProperties.KEY_ALGORITHM_EC, ANDROID_KEYSTORE
+        )
+
+        val builder = KeyGenParameterSpec.Builder(BIOMETRIC_ASYM_KEY_ALIAS,
+            KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY
+        )
+            .setDigests(
+                KeyProperties.DIGEST_SHA256,
+                KeyProperties.DIGEST_SHA512
+            )
+            .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP)
+            .setUserAuthenticationRequired(true)
+            .setUserAuthenticationValidityDurationSeconds(-1)
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+            builder.setUnlockedDeviceRequired(true)
+            if (context.packageManager.hasSystemFeature(PackageManager.FEATURE_STRONGBOX_KEYSTORE)) {
+                builder.setIsStrongBoxBacked(true)
+            }
+        }
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+            builder.setInvalidatedByBiometricEnrollment(true)
+        }
+        keyGenerator.initialize(builder.build())
+        keyGenerator.generateKeyPair()
+    }
+
+    fun getOrCreateBiometricSignature(context: Context): Signature {
+        val ks = KeyStore.getInstance(ANDROID_KEYSTORE)
+        ks.load(null)
+        if (!ks.containsAlias(BIOMETRIC_ASYM_KEY_ALIAS)) {
+            createAsymmetricKey(context)
+        }
+        val key = ks.getKey(BIOMETRIC_ASYM_KEY_ALIAS, null) as PrivateKey
+        val signature = Signature.getInstance(SIGNATURE_ALGORITHM)
+        signature.initSign(key)
+        return signature
+    }
+
+    fun verifySignature(data: ByteArray, signedData: ByteArray): Boolean {
+        val ks = KeyStore.getInstance(ANDROID_KEYSTORE)
+        ks.load(null)
+        val certificate = ks.getCertificate(BIOMETRIC_ASYM_KEY_ALIAS)
+        val signature = Signature.getInstance(SIGNATURE_ALGORITHM)
+        signature.initVerify(certificate)
+        signature.update(data)
+        return signature.verify(signedData)
+    }
+}
\ No newline at end of file