Notice
Recent Posts
Recent Comments
Link
투케이2K
151. (TWOK/UTIL) [Android/Java] C_SimpleAuth_Module : 휴대폰 간편 심플 인증 관련 모듈 본문
투케이2K 유틸파일
151. (TWOK/UTIL) [Android/Java] C_SimpleAuth_Module : 휴대폰 간편 심플 인증 관련 모듈
투케이2K 2024. 12. 13. 08:55[설 명]
프로그램 : Android / Java
설 명 : C_SimpleAuth_Module : 휴대폰 간편 심플 인증 관련 모듈
[소스 코드]
package com.example.javaproject.C_Module;
import static android.content.Context.FINGERPRINT_SERVICE;
import static android.content.Context.KEYGUARD_SERVICE;
import android.Manifest;
import android.app.AlertDialog;
import android.app.KeyguardManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Build;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.Looper;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore.KeyProperties;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.RequiresApi;
import androidx.core.content.ContextCompat;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import com.example.javaproject.S_Log;
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;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.core.ObservableEmitter;
public class C_SimpleAuth_Module {
/**
* // --------------------------------------------------------------------------------------
* TODO [클래스 설명]
* // --------------------------------------------------------------------------------------
* 1. [설명] : 휴대폰 간편 심플 인증 관련 모듈
* // --------------------------------------------------------------------------------------
* 2. 필요 퍼미션 :
*
* <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
* <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
* <uses-permission android:name="android.permission.USE_FINGERPRINT" />
* <uses-permission android:name="android.permission.USE_BIOMETRIC"/>
* // --------------------------------------------------------------------------------------
* 3. 제약 조건 :
*
* 안드로이드 디바이스 기기내 지문인증 기능을 사용하기 위해서는 마시멜로 버전 이상이어야합니다
* 지문인증 기능을 사용하기 위해서는 안드로이드 시스템 설정 내에 보안 설정 >> 잠금 설정 >> 지문 설정이되어야합니다
* // --------------------------------------------------------------------------------------
* */
/**
* // --------------------------------------------------------------------------------------
* TODO [빠른 로직 찾기 : 주석 로직 찾기]
* // --------------------------------------------------------------------------------------
* [SEARCH FAST] : observableSimpleAuth : 간편 인증 수행 실시
* // --------------------------------------------------------------------------------------
*
* // --------------------------------------------------------------------------------------
*
* // --------------------------------------------------------------------------------------
*
* // --------------------------------------------------------------------------------------
*/
// ------------------------------------------------------------------------------------------
// TODO [전역 변수 선언]
// ------------------------------------------------------------------------------------------
private static String ACTIVITY_NAME = "C_SimpleAuth_Module";
private static ObservableEmitter returnData = null; // [인증 결과 값 반환 변수 선언]
private static String errorMessage = ""; // [리턴 에러 메시지]
// TODO [지문 인증에 필요한 객체 선언 실시]
private static final String KEY_NAME = "simpleAuth_twok_key";
private static FingerprintManager fingerprintManager;
private static KeyguardManager keyguardManager;
private static KeyStore keyStore;
private static KeyGenerator keyGenerator;
public static Cipher cipher;
private static FingerprintManager.CryptoObject cryptoObject;
// TODO [메시지 표시 내용 정의]
private static final String ERROR_USE_NO_DEVICE = "지문을 사용할 수 없는 디바이스 입니다.";
private static final String ERROR_PERMISSION_IS_NO = "지문 인증 사용을 위한 권한을 허용해 주세요.";
private static final String ERROR_DEVICE_LOCK_NO = "지문 인증을 사용하기 위해서는 디바이스 잠금 화면을 설정해 주세요.";
private static final String ERROR_FINGER_REG_NO = "잠금 설정에 등록된 지문이 없습니다. 지문을 먼저 등록해주세요.";
private static final String ERROR_VERSION_NO_DEVICE = "지문을 사용할 수 없는 하위 버전의 디바이스 입니다.";
private static final String ING_FINGER_START_MSG = "손가락을 지문인식 센서에 대 주세요.";
private static final String FAIL_FINGER_AUTH = "지문 인증 실패 ... 다시 시도해주세요.";
private static final String SUCCESS_FINGER_AUTH = "지문 인증에 성공했습니다.";
private static final String SUCCESS_FINGER_AUTH = "지문 인증에 성공했습니다.";
private static final String ERROR_INIT_FAIL = "지문 인증 초기화에 문제가 발생 했습니다. (generateKey , cipherInit)";
private static final String EXCEPTION_MESSAGE = "지문 인증 진행 중 예외 문제가 발생 했습니다. (Exception)";
// ------------------------------------------------------------------------------------------
// TODO [SEARCH FAST] : observableSimpleAuth : 간편 인증 수행 실시
// ------------------------------------------------------------------------------------------
// TODO [호출 방법 소스 코드]
// -----------------------------------------------------------------------------------------
/*
try {
C_SimpleAuth_Module.observableSimpleAuth(A_Intro.this)
.subscribeOn(AndroidSchedulers.mainThread()) // [Observable (생성자) 로직을 IO 스레드에서 실행 : 백그라운드]
.observeOn(Schedulers.io()) // [Observer (관찰자) 로직을 메인 스레드에서 실행]
.subscribe(new Observer<Boolean>() { // [Observable.create 타입 지정]
@Override
public void onSubscribe(@NonNull Disposable d) {
}
@Override
public void onNext(@NonNull Boolean value) {
S_Log._D_(ACTIVITY_NAME + " :: observableSimpleAuth :: 간편 인증 수행 :: onNext", new String[]{String.valueOf(value)});
}
@Override
public void onError(@NonNull Throwable e) {
S_Log._E_(ACTIVITY_NAME + " :: observableSimpleAuth :: 간편 인증 수행 :: onError", new String[]{String.valueOf(e.getMessage())});
}
@Override
public void onComplete() {
}
});
}
catch (Exception e){
e.printStackTrace();
}
*/
// -----------------------------------------------------------------------------------------
public static Observable<Boolean> observableSimpleAuth(Context mContext){
// ===============================================================
S_Log._D_(ACTIVITY_NAME + " :: observableSimpleAuth :: 간편 인증 수행 실시", null);
// ===============================================================
return Observable.create(subscriber -> {
// ------------------------------------------------------
// TODO [ObservableEmitter 할당]
// ------------------------------------------------------
returnData = subscriber;
errorMessage = "";
// ------------------------------------------------------
// TODO [로직 처리 실시]
// ------------------------------------------------------
try {
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // TODO [안드로이드 마시멜로우 부터 사용 가능]
// TODO [Manifest.xml 에 Fingerprint 퍼미션을 추가해 워야 사용가능]
fingerprintManager = (FingerprintManager) mContext.getSystemService(FINGERPRINT_SERVICE);
keyguardManager = (KeyguardManager) mContext.getSystemService(KEYGUARD_SERVICE);
// TODO [지문을 사용할 수 없는 디바이스인 경우 체크 수행]
boolean deviceCheckFlag = false;
if(!fingerprintManager.isHardwareDetected()){
errorMessage = ERROR_USE_NO_DEVICE; // [ERROR] : 지문 인증을 사용할 수 없는 디바이스입니다.
}
else if(ContextCompat.checkSelfPermission(mContext, Manifest.permission.USE_FINGERPRINT) != PackageManager.PERMISSION_GRANTED){ // TODO [지문 인증 사용을 거부한 경우]
errorMessage = ERROR_PERMISSION_IS_NO; // [ERROR] : 지문 인증을 사용하기 위한 권한 부여 상태를 확인해주세요.
}
else if(!keyguardManager.isKeyguardSecure()){ // TODO [잠금 설정이 없는 경우]
errorMessage = ERROR_DEVICE_LOCK_NO; // [ERROR] : 잠금 설정 등록 여부를 확인해주세요.
}
else if(!fingerprintManager.hasEnrolledFingerprints()){ // TODO [잠금 설정에 등록된 지문이 없는 경우]
errorMessage = ERROR_FINGER_REG_NO; // [ERROR] : 잠금 설정에 등록된 지문 여부를 확인해주세요.
}
else { // TODO [모든 관문을 성공적으로 통과 (지문인식을 지원하고 지문 사용이 허용되어 있고 잠금화면이 설정되었고 지문이 등록되어 있을때)]
deviceCheckFlag = true;
}
// TODO [로직 분기 처리]
if (deviceCheckFlag == true){ // [디바이스 지원 및 잠금 정상 확인]
// [지문 인증 실행]
try {
boolean createKey = generateKey();
boolean initFlag = cipherInit();
if (createKey == true && initFlag == true){
// [CryptoObject 생성]
cryptoObject = new FingerprintManager.CryptoObject(cipher);
// TODO [지문 인증 실행]
C_FingerprintHandler fingerprintHandler = new C_FingerprintHandler(mContext);
fingerprintHandler.startAuth(fingerprintManager, cryptoObject);
}
else {
// [리턴 데이터 반환]
errorMessage = ERROR_INIT_FAIL; // [ERROR] : [지문 인증 초기화에 문제가 발생 했습니다. (generateKey , cipherInit)]
try {
if (returnData != null && returnData.isDisposed() == false){
returnData.onError(new Throwable(String.valueOf(errorMessage)));
returnData.onComplete();
returnData = null;
}
}
catch (Exception ew){}
}
}
catch (Exception e){
e.printStackTrace();
// [리턴 데이터 반환]
errorMessage = EXCEPTION_MESSAGE + " : [1] : " + String.valueOf(e.getMessage());
try {
if (returnData != null && returnData.isDisposed() == false){
returnData.onError(new Throwable(String.valueOf(errorMessage)));
returnData.onComplete();
returnData = null;
}
}
catch (Exception ew){}
}
}
else {
if (returnData != null && returnData.isDisposed() == false){
returnData.onError(new Throwable(String.valueOf(errorMessage)));
returnData.onComplete();
returnData = null;
}
}
}
else { // TODO [디바이스가 마시멜로 이하인 경우]
// [리턴 데이터 반환]
errorMessage = ERROR_VERSION_NO_DEVICE; // [ERROR] : Android Os Version Not Supported
if (returnData != null && returnData.isDisposed() == false){
returnData.onError(new Throwable(String.valueOf(errorMessage)));
returnData.onComplete();
returnData = null;
}
}
}
catch (final Exception e){
e.printStackTrace();
// [리턴 데이터 반환]
errorMessage = EXCEPTION_MESSAGE + " : [2] : " + String.valueOf(e.getMessage());
try {
if (returnData != null && returnData.isDisposed() == false){
returnData.onError(new Throwable(String.valueOf(errorMessage)));
returnData.onComplete();
returnData = null;
}
}
catch (Exception ew){}
}
});
}
// ------------------------------------------------------------------------------------------
// TODO [암호화 된 지문 관리자를 만드는 데 사용할 암호를 초기화 메소드]
// ------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------
@RequiresApi(api = Build.VERSION_CODES.M)
public static boolean cipherInit(){
// ===============================================================
S_Log._D_(ACTIVITY_NAME + " :: cipherInit :: 지문 인증 암호 초기화 수행 실시", null);
// ===============================================================
try {
// [Cipher.getInstance]
cipher = Cipher.getInstance(
KeyProperties.KEY_ALGORITHM_AES + "/"
+ KeyProperties.BLOCK_MODE_CBC + "/"
+ KeyProperties.ENCRYPTION_PADDING_PKCS7);
// [keyStore.load]
keyStore.load(null);
SecretKey key = (SecretKey) keyStore.getKey(KEY_NAME, null);
cipher.init(Cipher.ENCRYPT_MODE, key);
// [Return]
return true;
}
catch (Exception e) {
e.printStackTrace();
// [Log]
S_Log._E_(ACTIVITY_NAME + " :: cipherInit :: 지문 인증 암호 초기화 수행 에러", new String[]{"Error :: " + String.valueOf(e.getMessage())});
// [Return]
return false;
}
}
// ------------------------------------------------------------------------------------------
// TODO [비밀 키를 생성하는 메소드]
// ------------------------------------------------------------------------------------------
@RequiresApi(api = Build.VERSION_CODES.M)
protected static boolean generateKey() {
// ===============================================================
S_Log._D_(ACTIVITY_NAME + " :: generateKey :: 비밀 키 생성 실시", null);
// ===============================================================
try {
// [KeyStore.getInstance]
keyStore = KeyStore.getInstance("AndroidKeyStore");
// [KeyGenerator.getInstance]
keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
// [keyGenerator.generateKey]
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();
// [Return]
return true;
} catch (Exception e) {
e.printStackTrace();
// [Log]
S_Log._E_(ACTIVITY_NAME + " :: generateKey :: 비밀 키 생성 에러", new String[]{"Error :: " + String.valueOf(e.getMessage())});
// [Return]
return false;
}
}
// ------------------------------------------------------------------------------------------
// TODO [지문 인식 수행 부분] : [핸들러]
// ------------------------------------------------------------------------------------------
@RequiresApi(api = Build.VERSION_CODES.M)
public static class C_FingerprintHandler extends FingerprintManager.AuthenticationCallback {
// TODO [지문 인식 객체 선언]
CancellationSignal cancellationSignal;
private Context mContext;
// [클래스 생성자 초기화]
public C_FingerprintHandler(Context context){
this.mContext = context;
}
// [지문 인식 인증 시작 메소드]
@RequiresApi(api = Build.VERSION_CODES.M)
public void startAuth(FingerprintManager fingerprintManager, FingerprintManager.CryptoObject cryptoObject) {
// ===============================================================
S_Log._W_(ACTIVITY_NAME + " :: startAuth :: 지문 인증 시작 실시", new String[]{String.valueOf(ING_FINGER_START_MSG)});
// ===============================================================
try {
// [지문 인증 수행 실시]
cancellationSignal = new CancellationSignal();
fingerprintManager.authenticate(cryptoObject, cancellationSignal, 0, this, null);
// [토스트 메시지 표시]
try { if (this.mContext != null){ Toast.makeText(this.mContext, String.valueOf(ING_FINGER_START_MSG), Toast.LENGTH_SHORT).show(); } } catch (Exception es){}
}
catch (Exception e){
e.printStackTrace();
}
}
// [지문 인식 인증 에러 메소드]
@Override
public void onAuthenticationError(int errorCode, CharSequence errString) {
// ===============================================================
S_Log._E_(ACTIVITY_NAME + " :: onAuthenticationError :: 지문 인증 에러 발생", new String[]{"Error :: " + String.valueOf(errString)});
// ===============================================================
/**
* // -----------------------------------
* [주요 에러 메시지 정리]
* // -----------------------------------
* 1) 시도 횟수가 너무 많습니다. 나중에 다시 시도하세요.
* // -----------------------------------
* 2) 사용자가 지문 인식 작업을 취소했습니다.
* // -----------------------------------
*/
// [토스트 메시지 표시]
try { if (this.mContext != null){ Toast.makeText(this.mContext, String.valueOf(errString), Toast.LENGTH_SHORT).show(); } } catch (Exception es){}
// [인증 결과 처리]
this.update(String.valueOf(errString), false);
}
// [지문 인식 인증 실패 메소드]
@Override
public void onAuthenticationFailed() {
// ===============================================================
S_Log._E_(ACTIVITY_NAME + " :: onAuthenticationFailed :: 지문 인증 에러 발생", new String[]{"Error :: " + String.valueOf(FAIL_FINGER_AUTH)});
// ===============================================================
/**
* // -----------------------------------
* [주요 에러 메시지]
* // -----------------------------------
* 1) 지문 인증 실패. 다시 시도해주세요 (다른 손가락 지문)
* // -----------------------------------
* 2) 지문이 일치하지 않습니다.
* // -----------------------------------
*/
// [토스트 메시지 표시]
try { if (this.mContext != null){ Toast.makeText(this.mContext, String.valueOf(FAIL_FINGER_AUTH), Toast.LENGTH_SHORT).show(); } } catch (Exception es){}
}
// [지문 인식 인증 에러 메소드]
@Override
public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
// ===============================================================
S_Log._E_(ACTIVITY_NAME + " :: onAuthenticationHelp :: 지문 인증 실패", new String[]{"Error :: " + String.valueOf(helpString)});
// ===============================================================
/**
* // -----------------------------------
* [주요 에러 메시지]
* // -----------------------------------
* 1) 손가락을 너무 빨리 움직였습니다. 다시 시도해 주세요.
* // -----------------------------------
* 2) 지문 센서를 깨끗이 닦고 다시 시도하세요.
* // -----------------------------------
*/
// [토스트 메시지 표시]
try { if (this.mContext != null){ Toast.makeText(this.mContext, String.valueOf(helpString), Toast.LENGTH_SHORT).show(); } } catch (Exception es){}
}
// [지문 인식 인증 성공 메소드]
@Override
public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
// ===============================================================
S_Log._W_(ACTIVITY_NAME + " :: onAuthenticationSucceeded :: 지문 인증 성공", null);
// ===============================================================
// TODO [인증 결과 처리]
this.update(SUCCESS_FINGER_AUTH, true);
}
// [지문 인식 인증 중도 취소 메소드]
public void stopFingerAuth(){
// ===============================================================
S_Log._E_(ACTIVITY_NAME + " :: stopFingerAuth :: 지문 인증 중도 취소", null);
// ===============================================================
try {
if(cancellationSignal != null && !cancellationSignal.isCanceled()){
cancellationSignal.cancel();
}
}
catch (Exception e){
e.printStackTrace();
}
}
// [지문인식 인증 진행 후 동적 콘텐츠 변경 메소드]
private void update(String message, boolean success) {
// ===============================================================
S_Log._D_(ACTIVITY_NAME + " :: update :: 지문 인증 진행 확인", new String[]{
"success :: " + String.valueOf(success),
"message :: " + String.valueOf(message)
});
// ===============================================================
if(success == true) { // TODO [지문 인증 성공 한 경우]
// [콜백 반환]
if (returnData != null && returnData.isDisposed() == false){
returnData.onNext(true);
returnData.onComplete();
returnData = null;
}
}
else { // TODO [지문 인증 실패 한 경우]
// [콜백 반환]
if (returnData != null && returnData.isDisposed() == false){
returnData.onError(new Throwable(String.valueOf(message)));
returnData.onComplete();
returnData = null;
}
}
}
} // TODO [내부 클래스 종료]
} // TODO [클래스 종료]
반응형
'투케이2K 유틸파일' 카테고리의 다른 글
Comments