2018. 11. 6. 13:28ㆍAndroid
o AndroidManifest.xml
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
o MainActivity.java
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
private static final String KEY_NAME = "default_finger_key";
private Button fingerprint;
private TextView mResult;
/**
* 지문인증 절차
*
* 1. 프로젝트 Manifest 파일에 지문인증 권한을 요청
* 2. 잠금화면이 고정된 기기에만 지문등록이 가능하므로 잠금화면 설정이 되어 있는지 확인
* 3. 폰에 하나 이상의 지문이 등록 되어 있는지 확인
* 4. FingerprintManager 클래의 인스턴스 생성
* 5. Keystore 인스턴스로 안드로이드 Keystore 컨테이너에 액세스
* 6. KeyGenerator 클래스로 암호화된 키 생성하여 Keystre에 저장
* 7. 앞 단계에서 생성된 키를 이용 Cipher 클래스 인터턴스 초기화
* 8.Cipher 인스턴스를 사용하여 CryptoObject를 만들고 FingerprintManger 인스턴스에 지정
* 9. FingerprintManager 인스턴스의 authenticate 메소드를 호출
* 10. 인증 프로세스에 의해 트리거 된 콜백을 처리 메소드 구현
*/
private FingerprintManagerCompat fingerprintManager;
private KeyStore keyStore;
private KeyGenerator keyGenerator;
private FingerprintManagerCompat.CryptoObject cryptoObject; // over api 23
private BiometricPrompt.CryptoObject bioCryptoObject; // over api 28
@TargetApi(Build.VERSION_CODES.P)
@RequiresApi(api = Build.VERSION_CODES.M)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mResult = findViewById(R.id.result);
final FingerprintHandler fph = new FingerprintHandler(mResult);
fingerprint = findViewById(R.id.fingerprint);
if(checkFinger()){
mResult.setText("손가락을 대고 지문 인증을 하세요.");
generateKey();
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
bioCryptoObject = new BiometricPrompt.CryptoObject(generateCipher());
}else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
cryptoObject = new FingerprintManagerCompat.CryptoObject(generateCipher());
}
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
/**
* Callback structure provided to
* BiometricPrompt.authenticate(CancellationSignal, Executor, AuthenticationCallback) or
* BiometricPrompt.authenticate(CryptoObject, CancellationSignal, Executor, AuthenticationCallback).
* Users must provide an implementation of this for listening to authentication events.
*/
FingerprintOverPieHandler foph = new FingerprintOverPieHandler(mResult, MainActivity.this);
BiometricPrompt biometricPrompt = new BiometricPrompt.Builder(MainActivity.this).build();
foph.doAuth(biometricPrompt, bioCryptoObject);
}else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
fph.doAuth(fingerprintManager, cryptoObject);
}
}else{
fingerprint.setEnabled(false);
}
fingerprint.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
FingerprintOverPieHandler foph = new FingerprintOverPieHandler(mResult, MainActivity.this);
BiometricPrompt biometricPrompt = new BiometricPrompt.Builder(MainActivity.this).build();
foph.doAuth(biometricPrompt, bioCryptoObject);
}else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
fph.doAuth(fingerprintManager, cryptoObject);
}
}
});
}
@RequiresApi(api = Build.VERSION_CODES.M)
private boolean checkFinger() {
// Keyguard Manager
KeyguardManager keyguardManager = (KeyguardManager)getSystemService(KEYGUARD_SERVICE);
// Fingerprint Manager
fingerprintManager = FingerprintManagerCompat.from(this);
Log.d(TAG, "checkFinger : "+fingerprintManager.hasEnrolledFingerprints());
try {
// Check if the fingerprint sensor is present
if (!fingerprintManager.isHardwareDetected()) { // 지문 하드웨어 지원 여부
// Update the UI with a message
toastMessage(this, "Fingerprint authentication not supported");
return false;
}
if (!fingerprintManager.hasEnrolledFingerprints()) { // 지문 등록 여부
toastMessage(this, "Go to 'Settings -> Security -> Fingerprint' and register at least one" +
" fingerprint");
mResult.setText("등록된 지문이 없습니다. 최소 1개의 지문 등록이 필요합니다. 설정에서 지문 등록하세요.");
return false;
}
if (!keyguardManager.isKeyguardSecure()) { // 락스크린 존재 여부
toastMessage(this, "Secure lock screen not enabled");
return false;
}
}catch(SecurityException se) {
se.printStackTrace();
}
return true;
}
// AES
@RequiresApi(api = Build.VERSION_CODES.M)
private void generateKey() {
try {
// Get the reference to the key store
keyStore = KeyStore.getInstance("AndroidKeyStore");
// Key generator to generate the key
keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
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(KeyStoreException
| NoSuchAlgorithmException
| NoSuchProviderException
| InvalidAlgorithmParameterException
| CertificateException
| IOException exc) {
exc.printStackTrace();
}
}
// AES
private Cipher generateCipher() {
try {
Cipher cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
+ KeyProperties.BLOCK_MODE_CBC + "/"
+ KeyProperties.ENCRYPTION_PADDING_PKCS7);
SecretKey key = (SecretKey) keyStore.getKey(KEY_NAME, null);
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher;
}catch (NoSuchAlgorithmException
| NoSuchPaddingException
| InvalidKeyException
| UnrecoverableKeyException
| KeyStoreException exc) {
exc.printStackTrace();
}
return null;
}
// RSA
@RequiresApi(api = Build.VERSION_CODES.M)
private void generateKeyRSA() {
try {
keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
keyPairGenerator = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA,"AndroidKeyStore");
keyPairGenerator.initialize(
new KeyGenParameterSpec.Builder(KEY_NAME, KeyProperties.PURPOSE_DECRYPT)
.setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP)
.setUserAuthenticationRequired(true)
.build());
KeyPair keyPair = keyPairGenerator.generateKeyPair();
Log.d(TAG, "publicKey1 : "+keyPair.getPublic().toString());
} catch (KeyStoreException
| NoSuchAlgorithmException
| InvalidAlgorithmParameterException
| CertificateException
| NoSuchProviderException
| IOException e) {
e.printStackTrace();
}
}
// RSA
private Cipher generateCipherRSA() {
try {
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
PublicKey publicKey = keyStore.getCertificate(KEY_NAME).getPublicKey(); // set to server & get from server
OAEPParameterSpec spec = new OAEPParameterSpec(
KeyProperties.DIGEST_SHA256, "MGF1", MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT);
cipher.init(Cipher.ENCRYPT_MODE, publicKey, spec);
return cipher;
}catch (NoSuchAlgorithmException
| NoSuchPaddingException
| InvalidKeyException
| InvalidAlgorithmParameterException
| KeyStoreException exc) {
exc.printStackTrace();
}
return null;
}
public void toastMessage(Context context, String message){
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
}
public class FingerprintHandler extends FingerprintManagerCompat.AuthenticationCallback {
private int count = 0;
private TextView tv;
public FingerprintHandler(TextView tv) {
this.tv = tv;
}
@Override
public void onAuthenticationError(int errorCode, CharSequence errString) {
super.onAuthenticationError(errorCode, errString);
tv.setText("Auth error");
if(errString != null) {
tv.setText(errString);
count = 0;
}
toastMessage(MainActivity.this, "onAuthenticationError "+errorCode);
Log.d(TAG, "errString : "+errString);
}
@Override
public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
super.onAuthenticationHelp(helpCode, helpString);
}
@Override
public void onAuthenticationSucceeded(FingerprintManagerCompat.AuthenticationResult result) {
super.onAuthenticationSucceeded(result);
tv.setText("Auth ok");
tv.setTextColor(tv.getContext().getResources().getColor(android.R.color.holo_green_light));
Log.d(TAG, "Succeeded : "+result.getCryptoObject().getCipher().toString());
}
@Override
public void onAuthenticationFailed() {
super.onAuthenticationFailed();
toastMessage(MainActivity.this, "onAuthenticationFailed "+AUTH_RESULT);
tv.setText("다시 시도하세요.("+(++count)+")");
}
public void doAuth(FingerprintManagerCompat manager, FingerprintManagerCompat.CryptoObject crypto) {
CancellationSignal cancel = new CancellationSignal();
try {
manager.authenticate(crypto, 0, cancel, this, null);
} catch(SecurityException sce) {}
}
}
public class FingerprintOverPieHandler extends BiometricPrompt.AuthenticationCallback {
private static final String TAG = MainActivity.class.getSimpleName();
private Context mContext;
private TextView tv;
public FingerprintOverPieHandler(TextView tv, Context context) {
this.tv = tv;
this.mContext = context;
}
public FingerprintOverPieHandler() {
super();
}
@Override
public void onAuthenticationError(int errorCode, CharSequence errString) {
super.onAuthenticationError(errorCode, errString);
tv.setText("Auth error");
((MainActivity)mContext).toastMessage(mContext, "onAuthenticationError "+errorCode);
Log.d(TAG, "errString : "+errString);
}
@Override
public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
super.onAuthenticationHelp(helpCode, helpString);
}
@Override
public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result) {
super.onAuthenticationSucceeded(result);
tv.setText("auth ok");
tv.setTextColor(tv.getContext().getResources().getColor(android.R.color.holo_green_light));
}
@Override
public void onAuthenticationFailed() {
super.onAuthenticationFailed();
((MainActivity)mContext).toastMessage(mContext, "onAuthenticationFailed ");
}
public void doAuth(BiometricPrompt biometricPrompt, BiometricPrompt.CryptoObject crypto) {
android.os.CancellationSignal cancel = new android.os.CancellationSignal();
biometricPrompt.authenticate(crypto, cancel, null, this);
}
}
} // MainActivity end
https://smartstore.naver.com/byrollin?
'Android' 카테고리의 다른 글
roulette(룰렛) (0) | 2018.11.06 |
---|---|
Lock Pattern (0) | 2018.11.06 |
BarCode, QRCode&Scan (0) | 2018.11.06 |
NFC (0) | 2018.10.22 |
안드로이드 실행환경 (0) | 2018.10.19 |