I'm doing a project where I need to transfer the result of the fingerprint scan capture to a local server using NFC connection, and then receive an answer from the server.
So far I manage to understand how to do the transfer and scan the fringerprint using google's API, but the problem is that I can't get the scan result to send it for the server to complete the authentication.
I'm using google's fingerprint library from API23.
MainActivity:
package com.gmtechnology.smartalarm;
import android.Manifest;
import android.app.KeyguardManager;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.hardware.fingerprint.FingerprintManager;
import android.net.Uri;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.provider.Settings;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore.KeyProperties;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.NavigationView;
import android.support.v4.app.ActivityCompat;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import java.io.File;
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.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
public class MainActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener {
private static final String KEY_NAME = "main_key";
protected FingerprintManager fingerprintManager;
protected KeyguardManager keyguardManager;
private KeyStore keyStore;
protected KeyGenerator keyGenerator;
private Cipher cipher;
protected FingerprintManager.CryptoObject cryptoObject;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView state = (TextView) findViewById(R.id.state);
FloatingActionButton lock = (FloatingActionButton) findViewById(R.id.lock);
FloatingActionButton unlock = (FloatingActionButton) findViewById(R.id.unlock);
FloatingActionButton start = (FloatingActionButton) findViewById(R.id.start);
FloatingActionButton stop = (FloatingActionButton) findViewById(R.id.stop);
PackageManager pm = this.getPackageManager();
// Check whether NFC is available on device
if (!pm.hasSystemFeature(PackageManager.FEATURE_NFC)) {
// NFC is not available on the device.
Toast.makeText(this, "The device does not has NFC hardware",
Toast.LENGTH_SHORT).show();
}
// Check whether device is running Android 4.1 or higher
else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
// Android Beam feature is not supported.
Toast.makeText(this, "Wrong android version",
Toast.LENGTH_SHORT).show();
}
keyguardManager =
(KeyguardManager) getSystemService(KEYGUARD_SERVICE);
fingerprintManager =
(FingerprintManager) getSystemService(FINGERPRINT_SERVICE);
if (!keyguardManager.isKeyguardSecure()) {
Toast.makeText(this,
"Lock screen security not enabled in Settings",
Toast.LENGTH_LONG).show();
return;
}
if (ActivityCompat.checkSelfPermission(this,
Manifest.permission.USE_FINGERPRINT) !=
PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this,
"Fingerprint authentication permission not enabled",
Toast.LENGTH_LONG).show();
return;
}
if (!fingerprintManager.hasEnrolledFingerprints()) {
// This happens when no fingerprints are registered.
Toast.makeText(this,
"Register at least one fingerprint in Settings",
Toast.LENGTH_LONG).show();
return;
}
generateKey();
if (cipherInit()) {
cryptoObject = new FingerprintManager.CryptoObject(cipher);
//cryptoObject == the scanned fingerprint
FingerprintHandler helper = new FingerprintHandler(this);
helper.startAuth(fingerprintManager, cryptoObject, lock, unlock, start, stop, state);
}
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
drawer.setDrawerListener(toggle);
toggle.syncState();
NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
navigationView.setNavigationItemSelectedListener(this);
state(1, lock, unlock, start, stop, state);
}
public void sendFile(View view) {
NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
String command = "command";
if(!nfcAdapter.isEnabled()){
Toast.makeText(this, "Please enable NFC.",
Toast.LENGTH_SHORT).show();
startActivity(new Intent(Settings.ACTION_NFC_SETTINGS));
}
else if(!nfcAdapter.isNdefPushEnabled()) {
Toast.makeText(this, "Please enable Android Beam.",
Toast.LENGTH_SHORT).show();
startActivity(new Intent(Settings.ACTION_NFCSHARING_SETTINGS));
}
else {
if(view.getId() == R.id.lock) {
command = "lock";
}
else if(view.getId() == R.id.unlock) {
command = "unlock";
}
else if(view.getId() == R.id.start) {
command = "start";
}
else if(view.getId() == R.id.stop) {
command = "stop";
}
NdefRecord ndefRecord = NdefRecord.createMime("text/plain", command.getBytes());
NdefMessage ndefMessage = new NdefMessage(ndefRecord);
String fileName = "wallpaper.png";
// Retrieve the path to the user's public pictures directory
File fileDirectory = Environment
.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES);
// Create a new file using the specified directory and name
File fileToTransfer = new File(fileDirectory, fileName);
fileToTransfer.setReadable(true, false);
nfcAdapter.setBeamPushUris(
new Uri[]{Uri.fromFile(fileToTransfer)}, this);
nfcAdapter.setNdefPushMessage(ndefMessage, this);
}
}
protected void generateKey() {
try {
keyStore = KeyStore.getInstance("AndroidKeyStore");
} catch (Exception e) {
e.printStackTrace();
}
try {
keyGenerator = KeyGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_AES,
"AndroidKeyStore");
} catch (NoSuchAlgorithmException |
NoSuchProviderException e) {
throw new RuntimeException(
"Failed to get KeyGenerator instance", e);
}
try {
keyStore.load(null);
keyGenerator.init(new
KeyGenParameterSpec.Builder(KEY_NAME,
KeyProperties.PURPOSE_ENCRYPT |
KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setUserAuthenticationRequired(true)
.setEncryptionPaddings(
KeyProperties.ENCRYPTION_PADDING_PKCS7)
.build());
keyGenerator.generateKey();
} catch (NoSuchAlgorithmException |
InvalidAlgorithmParameterException
| CertificateException | IOException e) {
throw new RuntimeException(e);
}
}
public boolean cipherInit() {
try {
cipher = Cipher.getInstance(
KeyProperties.KEY_ALGORITHM_AES + "/"
+ KeyProperties.BLOCK_MODE_CBC + "/"
+ KeyProperties.ENCRYPTION_PADDING_PKCS7);
} catch (NoSuchAlgorithmException |
NoSuchPaddingException e) {
throw new RuntimeException("Failed to get Cipher", e);
}
try {
keyStore.load(null);
SecretKey key = (SecretKey) keyStore.getKey(KEY_NAME,
null);
cipher.init(Cipher.ENCRYPT_MODE, key);
return true;
} catch (KeyPermanentlyInvalidatedException e) {
return false;
} catch (KeyStoreException | CertificateException
| UnrecoverableKeyException | IOException
| NoSuchAlgorithmException | InvalidKeyException e) {
throw new RuntimeException("Failed to init Cipher", e);
}
}
@Override
public void onBackPressed() {
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
super.onBackPressed();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
@SuppressWarnings("StatementWithEmptyBody")
@Override
public boolean onNavigationItemSelected(MenuItem item) {
// Handle navigation view item clicks here.
int id = item.getItemId();
if (id == R.id.nav_camera) {
// Handle the camera action
} else if (id == R.id.nav_gallery) {
} else if (id == R.id.nav_share) {
} else if (id == R.id.nav_send) {
}
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
drawer.closeDrawer(GravityCompat.START);
return true;
}
public void state (int s, FloatingActionButton lock, FloatingActionButton unlock, FloatingActionButton start, FloatingActionButton stop, TextView state) {
if (s == 1) {
state.setText(R.string.scan);
state.setTextColor(0xFF970E0E);
lock.setEnabled(false);
lock.hide();
unlock.setEnabled(false);
unlock.hide();
start.setEnabled(false);
start.hide();
stop.setEnabled(false);
stop.hide();
}
if (s == 2) {
state.setText(R.string.done);
state.setTextColor(0xFF149926);
lock.setEnabled(true);
lock.show();
unlock.setEnabled(true);
unlock.show();
start.setEnabled(true);
start.show();
stop.setEnabled(true);
stop.show();
}
if (s == 3) {
state.setText(R.string.error);
state.setTextColor(0xFF970E0E);
lock.setEnabled(false);
lock.hide();
unlock.setEnabled(false);
unlock.hide();
start.setEnabled(false);
start.hide();
stop.setEnabled(false);
stop.hide();
}
}
}
FingerprintHandler:
package com.gmtechnology.smartalarm;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.fingerprint.FingerprintManager;
import android.os.CancellationSignal;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.ActivityCompat;
import android.widget.TextView;
public class FingerprintHandler extends
FingerprintManager.AuthenticationCallback {
protected CancellationSignal cancellationSignal;
private Context appContext;
private FloatingActionButton mlock;
private FloatingActionButton mstart;
private FloatingActionButton munlock;
private FloatingActionButton mstop;
private TextView mstate;
public FingerprintHandler(Context context) {
appContext = context;
}
public void startAuth(FingerprintManager manager,
FingerprintManager.CryptoObject cryptoObject, FloatingActionButton lock, FloatingActionButton unlock, FloatingActionButton start, FloatingActionButton stop, TextView state) {
mlock = lock;
munlock = unlock;
mstart = start;
mstop = stop;
mstate = state;
cancellationSignal = new CancellationSignal();
if (ActivityCompat.checkSelfPermission(appContext,
Manifest.permission.USE_FINGERPRINT) !=
PackageManager.PERMISSION_GRANTED) {
return;
}
manager.authenticate(cryptoObject, cancellationSignal, 0, this, null);
}
@Override
public void onAuthenticationHelp(int helpMsgId,
CharSequence helpString) {
new MainActivity().state(3, mlock, munlock, mstart, mstop, mstate);
}
@Override
public void onAuthenticationFailed() {
new MainActivity().state(3, mlock, munlock, mstart, mstop, mstate);
}
@Override
public void onAuthenticationSucceeded(
FingerprintManager.AuthenticationResult result) {
new MainActivity().state(2, mlock, munlock, mstart, mstop, mstate);
}
}
If anyone could point me out how I can capture and convert the scan result to a format where I can send it via NFC that would be awesome!
PS: I'm not a programmer, please go easy on me T^T
restating the possibilities based on Micheal answers: Would there be anyway to directly connecting the sensor to the server using NFC so that the data can be directly send to the server for authentication without saving it? The whole idea is that the authentication is done by an outside server not the smartphone, this way making the smartphone an "universal key", so that ANY smartphone can be used to authenticate to any server, using the correct application of course, or maybe somehow trick the smartphone into using the database connect via NFC to compare the fingerprint, this way the current information is on a safe memory, but the database used to compare the fingerprint is on a remote server. I know those sound tricky, maybe even "crazy", but if there is a possibility to do it even outside of "proper" ways that would be enought, it's only for reaserch and study propouses.
CryptoObject
associated with the user, and use it to sign some data that you send to the server). – Latimore