java 从智能手机捕获指纹并保存到文件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/35934729/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Capture fingerprint from smartphone and save to a file
提问by Giovanni Marcolla
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.
我正在做一个项目,我需要使用 NFC 连接将指纹扫描捕获的结果传输到本地服务器,然后从服务器接收答案。
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.
到目前为止,我设法了解如何使用谷歌的 API 进行传输和扫描边缘指纹,但问题是我无法获得扫描结果以将其发送给服务器以完成身份验证。
I'm using google's fingerprint library from API23.
我正在使用来自 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!
如果有人能指出我如何捕获扫描结果并将其转换为可以通过 NFC 发送的格式,那就太棒了!
PS: I'm not a programmer, please go easy on me T^T
PS:我不是程序员,请放轻松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.
重申基于 Micheal 回答的可能性:是否可以使用 NFC 将传感器直接连接到服务器,以便数据可以直接发送到服务器进行身份验证而不保存?整个想法是,身份验证是由外部服务器而不是智能手机完成的,这样使智能手机成为“通用密钥”,这样任何智能手机都可以用来向任何服务器进行身份验证,当然,使用正确的应用程序,或者也许以某种方式欺骗智能手机使用通过 NFC 连接的数据库来比较指纹,这样当前信息在安全内存中,但用于比较指纹的数据库在远程服务器上。我知道那些听起来很棘手,甚至可能是“疯狂的”,但如果有可能在“适当的”之外做这件事
回答by divegeek
Android fingerprint authentication is designed specifically to make it impossible to do what you want.
Android 指纹认证专门设计用于让您无法为所欲为。
The design ensures that fingerprint image data is never available within the Android OS at all. Even if you root the device or compromise the kernel, this data is simply not available. Fingerprint images are required to be passed securely from the fingerprint scanner to the secure hardware that does the fingerprint matching. This is actually an Android compliance requirement. The reason is that fingerprints are important personal information which should not be allowed to leak.
该设计确保指纹图像数据在 Android 操作系统中根本不可用。即使您 root 设备或破坏内核,这些数据也根本不可用。指纹图像需要安全地从指纹扫描仪传递到进行指纹匹配的安全硬件。这实际上是 Android 合规性要求。原因是指纹是重要的个人信息,不能泄露。
Regarding your alternate suggestion, I can't image why any manufacturer would make that possible.
关于您的替代建议,我无法想象为什么任何制造商都能做到这一点。
If you want to use Android as a universal key, you should do something like:
如果您想使用 Android 作为通用密钥,您应该执行以下操作:
- Generate a public/private key pair on the device with AndroidKeyStore (I recommend EC), specifying that the key may only be used with fingerprint authentication.
- Extract the public key and register it with the server.
- To "unlock" with your universal key, conduct a challenge-response protocol with the server, like:
- Generate a random value on the server, say, 32 bytes.
- Send the random value to the phone.
- Create a
Signature
object with your AndroidKeyStore key, init it and wrap it in aFingerprintManager.CryptoObject
. - Do fingerprint authentication and when it completes sign the random value.
- Send the signed random value to the server.
- Have the server verify the random value and signature.
- 使用AndroidKeyStore(我推荐EC)在设备上生成公钥/私钥对,指定密钥只能用于指纹认证。
- 提取公钥并将其注册到服务器。
- 要使用通用密钥“解锁”,请与服务器执行质询-响应协议,例如:
- 在服务器上生成一个随机值,比如 32 字节。
- 将随机值发送到手机。
Signature
使用您的 AndroidKeyStore 密钥创建一个对象,初始化它并将其包装在FingerprintManager.CryptoObject
.- 进行指纹认证,完成后对随机值进行签名。
- 将签名的随机值发送到服务器。
- 让服务器验证随机值和签名。
If you want to do fingerprint verification on the server you'll need to get some other sort of fingerprint scanner. You can't do this with an Android (or iOS) phone.
如果您想在服务器上进行指纹验证,则需要使用其他类型的指纹扫描仪。您无法使用 Android(或 iOS)手机执行此操作。