[Android] 생체 인증 정리 ( FingerprintManager, BiometricPrompt )

kkatal_chae·2022년 9월 22일
0

[Android] 생체 인증

목록 보기
1/1
post-thumbnail

안드로이드 생체 인증을 구현해보기 위해 작성한 글, 코드기 때문에 참고만 하시고 혹시 문제가 있는 부분이 있다면 말씀해주세요 !!

안드로이드에서 생체 인증을 사용할 수 있던 것은 API Level 23 ( Android 6.0 ) 부터이다.

이 때는 fingerprintManager 라는 녀석을 사용했다.

하지만 API level 28 ( Android 9.0 ) 에 와서는 지문만이 아닌 다른 생체 인증 방식이 생겨나면서 deprecated 되었다.

이후부터는 BiometricPrompt 라는 녀석을 사용하게 된다.

먼저 fingerprintManager 를 살펴보도록 하자

fingerprintManager

우선 지문 사용에 대한 권한을 허용해줘야 한다.

AndroidManifest.xml

<uses-permission android:name="android.permission.USE_FINGERPRINT" />

다음은 fingerprintManager 를 사용해 인증을 하는 코드입니다.

fingerprintManager 는 암호화된FingerprintManager.CryptoObject 를 인자로 받는다. 때문에 별도로 암호화 과정이 필요하다.

BioAuthManager.java

// api 23 ( ANDROID 6.0 ) 부터 api 28 ( ANDROID 9.0 ) 까지는 fingerprint 사용
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
{
	
	fingerprintManager = (FingerprintManager) context.getSystemService(FINGERPRINT_SERVICE);
  keyguardManager = (KeyguardManager) context.getSystemService(KEYGUARD_SERVICE);
  String comment = "";

	// 지문을 사용할 수 없는 디바이스인 경우
  if (!fingerprintManager.isHardwareDetected()) 
  {
	  Log.d("fingerprint", "it is not device that can use fingerprint");
  }
  // 지문 인증 사용을 거부한 경우
  else if (ContextCompat.checkSelfPermission(context, Manifest.permission.USE_FINGERPRINT) != PackageManager.PERMISSION_GRANTED)
  {
		 Log.d("fingerprint", "permission denied");
  }
	// 잠금 화면이 설정되지 않은 경우
  else if (!keyguardManager.isKeyguardSecure()) 
  {
	  Log.d("fingerprint", "please set lock screen");
  }
	// 등록된 지문이 없는 경우
  else if (!fingerprintManager.hasEnrolledFingerprints())
  {
	  Log.d("fingerprint", "please enroll fingerprint");
  }
  else
  {
	  Log.d("fingerprint", "requirement fingerprint needed all pass");
    keyManager.generateKey();

    if (keyManager.cipherInit())
    {

	    Cipher cipher = keyManager.getCipher();
      CancellationSignal signal = keyManager.getSignal();

      cryptoObject = new FingerprintManager.CryptoObject(cipher);

      fingerprintManager.authenticate(cryptoObject, signal, 0, new FingerprintManager.AuthenticationCallback() {
	      @Override
	      public void onAuthenticationError(int errorCode, CharSequence errString) {
	        super.onAuthenticationError(errorCode, errString);
          Log.d("fingerprint", String.valueOf(errorCode));
        }

        @Override
        public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
	        super.onAuthenticationSucceeded(result);
          Log.d("fingerprint", "auth success");

        }

        @Override
        public void onAuthenticationFailed() {
	        super.onAuthenticationFailed();
          Log.d("fingerprint", "auth failed");

        }

      }, null);
    }

  }

암호화된 FingerprintManager.CryptoObject 를 제공하기 위한 별도의 클래스를 만들어서 제공하였다.

KeyManager

public class KeyManager {

    private static KeyManager instance;

    private KeyManager() {
        signal = new CancellationSignal();
    };

    public static KeyManager getInstance() {
        if (instance == null)
        {
            instance = new KeyManager();
        }

        return instance;
    }

    private static final String KEY_NAME = "BIOAUTH_KEY";
    private KeyStore keyStore;
    private KeyGenerator keyGenerator;

    @Getter @Setter
    private Cipher cipher;

    @Getter @Setter
    private CancellationSignal signal;

    /**
     * 지문 인증을 사용하기 위한 키를 생성하는 함수
     */
    public void generateKey() {
        try {

            // 안드로이드에서 기본적으로 제공하는 KeyStore 인듯하다 ( AndroidKeyStore )
            keyStore = KeyStore.getInstance("AndroidKeyStore");
            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 (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 키스토어 저장된 키를 암호화하는 함수
     * @return Boolean
     */
    public Boolean cipherInit() {
        try {
            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 true;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return false;
    }
}

biometricPrompt

API level 28 ( Android 9.0 ) 부터는 biometricPrompt 를 사용하여야 한다.

이 또한 생체 정보를 사용하기 위한 권한을 허용해주어야 한다.

AndroidManifest.xml

<uses-permission android:name="android.permission.USE_BIOMETRIC" />

또한 , gradle 에 의존성을 추가해주어야 한다.

build.gradle(:app)

// 2022.09.22 기준 최신버전 
dependencies {
	implementation 'androidx.biometric:biometric:1.1.0'
}

다음은 BiometricPrompt 를 사용하여 생체 인증을 진행하는 코드이다.

BioAuthManager

// api 28 ( ANDROID 9.0 ) 이상은 biometricPrompt 사용
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
 

            executor = ContextCompat.getMainExecutor(context);
            biometricPrompt = new BiometricPrompt(activity, executor, new BiometricPrompt.AuthenticationCallback() {
                @Override
                public void onAuthenticationError(int errorCode, @NonNull CharSequence errString) {
                    super.onAuthenticationError(errorCode, errString);
                    Log.d("bioAuth", errString.toString());
                }

                @Override
                public void onAuthenticationSucceeded(@NonNull BiometricPrompt.AuthenticationResult result) {
                    super.onAuthenticationSucceeded(result);
                    Log.d("bioAuth", "auth success");
                }

                @Override
                public void onAuthenticationFailed() {
                    super.onAuthenticationFailed();
                    Log.d("bioAuth", "auth failed");
                }
            });

            promptInfo = new BiometricPrompt.PromptInfo.Builder()
                    .setTitle("지문 인증")
                    .setSubtitle("기기에 등록된 지문을 이용하여 지문을 인증해주세요.")
                    .setDescription("생체 인증 설명")
                    // BIOMETRIC_STRONG 은 안드로이드 11 에서 정의한 클래스 3 생체 인식을 사용하는 인증 - 암호화된 키 필요 
                    // BIOMETRIC_WEAK 은 안드로이드 11 에서 정의한 클래스 2 생체 인식을 사용하는 인증 - 암호화된 키까지는 불필요 
                    // DEVICE_CREDENTIAL 은 화면 잠금 사용자 인증 정보를 사용하는 인증 - 사용자의 PIN, 패턴 또는 비밀번호
                    .setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_WEAK)
                    .setConfirmationRequired(false) // 명시적인 사용자 작업 ( 생체 인식 전 한번더 체크 ) 없이 인증할건지 default : true
                    .setNegativeButtonText("취소")
                    .build();

			keyManager.generateKey();

            if (keyManager.cipherInit())
            {
                bioCryptoObject = new BiometricPrompt.CryptoObject(keyManager.getCipher());
                biometricPrompt.authenticate(promptInfo, bioCryptoObject);
            }
			
            biometricPrompt.authenticate(promptInfo);

        }

전체코드는 깃허브를 참조하여 주세요 .

https://github.com/kkatalchae/Android_BioAuthSample

0개의 댓글