Android指纹解锁

public class FingerprintActivity extends Activity implements OnClickListener {

    private FingerprintManager manager;
    private CancellationSignal signal;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fingerprint);
        findViewById(R.id.Fingerprint).setOnClickListener(this);
        manager = (FingerprintManager) getSystemService(Context.FINGERPRINT_SERVICE);
    }

    @SuppressLint("NewApi")
    @Override
    public void onClick(View arg0) {
        switch (arg0.getId()) {
        case R.id.Fingerprint:

            if (!manager.isHardwareDetected()) {
                Toast.makeText(this, "设备不支持", Toast.LENGTH_LONG).show();
                return;
            }
            KeyguardManager keyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
            if (!keyguardManager.isKeyguardSecure()) {
                Toast.makeText(this, "请设置图案锁", Toast.LENGTH_LONG).show();
                return;
            }
            if (!manager.hasEnrolledFingerprints()) {
                Toast.makeText(this, "请设置指纹", Toast.LENGTH_LONG).show();
                return;
            }

            try {
                CryptoObjectHelper helper = new CryptoObjectHelper();
                signal = new CancellationSignal();
                /**
                 *FingerprintManager.authenticate(CryptoObject crypto, 
                 * CancellationSignal cancel, 
                 * int flags, AuthenticationCallback callback,
                 *  Handler handler)参数解释
                 *  1. crypto这是一个加密类的对象,指纹扫描器会使用这个对象来判断认证结果的合法性。
                 *  这个对象可以是null,但是这样的话,就意味这app无条件信任认证的结果,
                 *  虽然从理论上这个过程可能被攻击,数据可以被篡改,
                 *  这是app在这种情况下必须承担的风险。因此,建议这个参数不要置为null。
                 *  这个类的实例化有点麻烦,主要使用javax的security接口实现
                 * CryptoObjectHelper为写好的工具类直接引用即可
                 * 2.cancel 这个是CancellationSignal类的一个对象,
                 * 这个对象用来在指纹识别器扫描用户指纹的是时候取消当前的扫描操作,
                 * 如果不取消的话,那么指纹扫描器会移植扫描直到超时(一般为30s,取决于具体的厂商实现),
                 * 这样的话就会比较耗电。建议这个参数不要置为null。 
                 * 3.flags 标识位,根据上图的文档描述,这个位暂时应该为0,这个标志位应该是保留将来使用的。
                 * 4.callback 这个是FingerprintManager.AuthenticationCallback类的对象,
                 * 这个是这个接口中除了第一个参数之外最重要的参数了。
                 * 当系统完成了指纹认证过程(失败或者成功都会)后,
                 * 会回调这个对象中的接口,通知app认证的结果。这个参数不能为NULL
                 * 5.handler 这是Handler类的对象,如果这个参数不为null的话,
                 * 那么FingerprintManager将会使用这个handler中的looper来处理来自指纹识别硬件的消息。
                 * 通常来讲,开发这不用提供这个参数,可以直接置为null,
                 * 因为FingerprintManager会默认使用app的main looper来处理。
                 * 
                 * */
                manager.authenticate(helper.buildCryptoObject(), signal, 0,new MyAuthCallback(FingerprintActivity.this), null);

            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            break;

        default:
            break;
        }

    }
    @SuppressLint("NewApi")
    @Override
    protected void onStop() {
        // TODO Auto-generated method stub
        super.onStop();
        signal.cancel();
    }


}
 

MyAuthCallback 类

public class MyAuthCallback extends AuthenticationCallback{

    private Context context;
    public MyAuthCallback(Context context){
        this.context = context;
    }

    /**
     * 这个接口会在认证成功之后回调。我们可以在这个方法中提示用户认证成功。
     * 这里需要说明一下,如果我们上面在调用authenticate的时候,我们的CryptoObject不是null的话,
     * 那么我们在这个方法中可以通过AuthenticationResult来获得Cypher对象然后调用它的doFinal方法。
     * doFinal方法会检查结果是不是会拦截或者篡改过,如果是的话会抛出一个异常。
     * 当我们发现这些异常的时候都应该将认证当做是失败来来处理,为了安全建议大家都这么做。 
     * 
     * */
    @Override
    public void onAuthenticationSucceeded(AuthenticationResult result) {
        // TODO Auto-generated method stub
        super.onAuthenticationSucceeded(result);
        Toast.makeText(context, "成功", Toast.LENGTH_LONG).show();
        //AuthenticationResult来获得Cypher对象然后调用它的doFinal方法。
        //doFinal方法会检查结果是不是会拦截或者篡改过,如果是的话会抛出一个异常。
        //当我们发现这些异常的时候都应该将认证当做是失败来来处理,为了安全建议大家都这么做。 
        try {
            byte [] by =result.getCryptoObject().getCipher().doFinal();
            String date  = new String(by, "UTF-8");

            Log.e("TAG", date);

        }  catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    /**
     * 
     * 这个接口会在系统指纹认证失败的情况的下才会回调。注意这里的认证失败和上面的认证错误是不一样的,
     * 虽然结果都是不能认证。认证失败是指所有的信息都采集完整,并且没有任何异常,
     * 但是这个指纹和之前注册的指纹是不相符的;但是认证错误是指在采集或者认证的过程中出现了错误,
     * 比如指纹传感器工作异常等。也就是说认证失败是一个可以预期的正常情况,而认证错误是不可预期的异常情况。 
     * 
     * */
    @Override
    public void onAuthenticationFailed() {
        // TODO Auto-generated method stub
        super.onAuthenticationFailed();
        Toast.makeText(context, "认证失败", Toast.LENGTH_LONG).show();
    }
    /**
     * 上面的认证失败是认证过程中的一个异常情况,我们说那种情况是因为出现了不可恢复的错误,
     * 而我们这里的OnAuthenticationHelp方法是出现了可以回复的异常才会调用的。
     * 什么是可以恢复的异常呢?一个常见的例子就是:
     * 手指移动太快,当我们把手指放到传感器上的时候,如果我们很快地将手指移走的话,
     * 那么指纹传感器可能只采集了部分的信息,因此认证会失败。
     * 但是这个错误是可以恢复的,因此只要提示用户再次按下指纹,并且不要太快移走就可以解决。
     * 
     * */
    @Override
    public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
        // TODO Auto-generated method stub
        super.onAuthenticationHelp(helpCode, helpString);
        Toast.makeText(context, "再次按下指纹", Toast.LENGTH_LONG).show();
    }
    /**
     * 这个接口会再系统指纹认证出现不可恢复的错误的时候才会调用,并且参数errorCode就给出了错误码,
     * 标识了错误的原因。这个时候app能做的只能是提示用户重新尝试一遍。 
     * 
     * **/
    @Override
    public void onAuthenticationError(int errorCode, CharSequence errString) {
        // TODO Auto-generated method stub
        super.onAuthenticationError(errorCode, errString);
        Toast.makeText(context, "认证异常", Toast.LENGTH_LONG).show();
    }

}
 

/**
 * 参考链接:http://blog.csdn.net/baniel01/article/details/51991764
 * 
 * **/

public class CryptoObjectHelper {
    // This can be key name you want. Should be unique for the app.
    static final String KEY_NAME = "com.createchance.android.sample.fingerprint_authentication_key";

    // We always use this keystore on Android.
    static final String KEYSTORE_NAME = "AndroidKeyStore";

    // Should be no need to change these values.
    static final String KEY_ALGORITHM = KeyProperties.KEY_ALGORITHM_AES;
    static final String BLOCK_MODE = KeyProperties.BLOCK_MODE_CBC;
    static final String ENCRYPTION_PADDING = KeyProperties.ENCRYPTION_PADDING_PKCS7;
    static final String TRANSFORMATION = KEY_ALGORITHM + "/" +
            BLOCK_MODE + "/" +
            ENCRYPTION_PADDING;
    final KeyStore _keystore;

    public CryptoObjectHelper() throws Exception
    {
        _keystore = KeyStore.getInstance(KEYSTORE_NAME);
        _keystore.load(null);
    }

    //FingerprintManagerCompat.CryptoObject buildCryptoObject() 
    public FingerprintManager.CryptoObject buildCryptoObject() throws Exception{
        Cipher cipher = createCipher(true);
        return new FingerprintManager.CryptoObject(cipher);
    }

    Cipher createCipher(boolean retry) throws Exception{
        Key key = GetKey();
        Cipher cipher = Cipher.getInstance(TRANSFORMATION);
        try
        {
            cipher.init(Cipher.ENCRYPT_MODE | Cipher.DECRYPT_MODE, key);
        } catch(KeyPermanentlyInvalidatedException e)
        {
            _keystore.deleteEntry(KEY_NAME);
            if(retry)
            {
                createCipher(false);
            } else
            {
                throw new Exception("Could not create the cipher for fingerprint authentication.", e);
            }
        }
        return cipher;
    }

    Key GetKey() throws Exception
    {
        Key secretKey;
        if(!_keystore.isKeyEntry(KEY_NAME))
        {
            CreateKey();
        }

        secretKey = _keystore.getKey(KEY_NAME, null);
        return secretKey;
    }

    void CreateKey() throws Exception
    {
        KeyGenerator keyGen = KeyGenerator.getInstance(KEY_ALGORITHM, KEYSTORE_NAME);
        KeyGenParameterSpec keyGenSpec =
                new KeyGenParameterSpec.Builder(KEY_NAME, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
        .setBlockModes(BLOCK_MODE)
        .setEncryptionPaddings(ENCRYPTION_PADDING)
        .setUserAuthenticationRequired(true)
        .build();
        keyGen.init(keyGenSpec);
        keyGen.generateKey();
    }
}

地址:https://git.oschina.net/qswwers/Android-Fingerprint.git

猜你喜欢

转载自my.oschina.net/u/2355512/blog/798827