· 7 years ago · Aug 23, 2018, 09:06 AM
1public class FingerprintDialogFragment extends BottomSheetDialogFragment {
2
3 private static final String TAG = FingerprintDialogFragment.class.getSimpleName();
4 private static final String TITLE = "title";
5 private static final String SUBJECT = "subject";
6 private static final String MODE = "mode";
7
8 public static final int MODE_ENCRYPT = Cipher.ENCRYPT_MODE;
9 public static final int MODE_DECRYPT = Cipher.DECRYPT_MODE;
10
11 private static final String KEY_ALIAS = "any-alias";
12 private static final String KEY_STORE = "AndroidKeyStore";
13 private static final String SHARED_PREFERENCE_KEY_PASS = "keypass";
14 private static final String SHARED_PREFERENCE_KEY_IV = "iv";
15
16 private TextView titleTextView;
17 private TextView errorTextView;
18 private AppCompatButton cancelButton;
19 private AppCompatButton usePasswordButton;
20
21 private String title;
22 private String subject;
23 private int mode;
24
25 private KeyStore keyStore;
26 private SecretKey secretKey;
27 private Cipher cipher;
28
29 private CancellationSignal cancellationSignal;
30 private FingerprintManager fingerprintManager;
31
32 private FingerprintAuthListener listener;
33
34 public static FingerprintDialogFragment newInstance(String title, String subject, int mode) {
35 FingerprintDialogFragment fragment = new FingerprintDialogFragment();
36 Bundle args = new Bundle();
37 args.putString(TITLE, title);
38 args.putString(SUBJECT, subject);
39 args.putInt(MODE, mode);
40
41 fragment.setArguments(args);
42 return fragment;
43 }
44
45 public interface FingerprintAuthListener {
46 void onAuthenticationSucceeded(String result);
47 void onCancel();
48 void onDisable();
49 }
50
51 public void setFingerprintAuthListener(FingerprintAuthListener listener) {
52 this.listener = listener;
53 }
54
55 @RequiresApi(Build.VERSION_CODES.M)
56 private class AuthenticationCallback extends FingerprintManager.AuthenticationCallback {
57
58 public AuthenticationCallback() {
59 }
60
61 @Override
62 public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
63 Cipher cipher = result.getCryptoObject().getCipher();
64
65 if (mode == Cipher.ENCRYPT_MODE) {
66 encryptResult(cipher);
67 } else {
68 decryptResult(cipher);
69 }
70 dismiss();
71 }
72
73 private void encryptResult(Cipher cipher) {
74 try {
75 byte[] bytes = cipher.doFinal(subject.getBytes());
76 String encrypted = Base64.encodeToString(bytes, Base64.NO_WRAP);
77
78 if (listener != null) {
79 listener.onAuthenticationSucceeded(encrypted);
80 }
81 } catch (IllegalBlockSizeException e) {
82 //
83 } catch (BadPaddingException e) {
84 //
85 }
86 }
87
88 private void decryptResult(Cipher cipher) {
89 try {
90 byte[] bytes = Base64.decode(subject, Base64.NO_WRAP);
91 String decrypted = new String(cipher.doFinal(bytes));
92
93 if (listener != null) {
94 listener.onAuthenticationSucceeded(decrypted);
95 }
96 } catch (IllegalBlockSizeException e) {
97 //
98 } catch (BadPaddingException e) {
99 //
100 }
101 }
102
103 @Override
104 public void onAuthenticationError(int errMsgId, CharSequence errString) {
105 errorTextView.setVisibility(View.VISIBLE);
106 }
107
108 @Override
109 public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
110 errorTextView.setVisibility(View.VISIBLE);
111 }
112
113 @Override
114 public void onAuthenticationFailed() {
115 errorTextView.setVisibility(View.VISIBLE);
116 }
117 }
118
119 @RequiresApi(Build.VERSION_CODES.M)
120 private boolean initCipher(Context context, int mode) throws InvalidKeyException {
121
122 try {
123 cipher = Cipher.getInstance(
124 KeyProperties.KEY_ALGORITHM_AES + "/"
125 + KeyProperties.BLOCK_MODE_CBC + "/"
126 + KeyProperties.ENCRYPTION_PADDING_PKCS7);
127
128 if (mode == Cipher.ENCRYPT_MODE) {
129 cipher.init(Cipher.ENCRYPT_MODE, secretKey);
130
131 PreferenceManager.getDefaultSharedPreferences(context)
132 .edit().putString(SHARED_PREFERENCE_KEY_IV, Base64.encodeToString(cipher.getIV(), Base64.NO_WRAP))
133 .apply();
134
135 return true;
136
137 } else {
138
139 String keyIV = PreferenceManager.getDefaultSharedPreferences(context)
140 .getString(SHARED_PREFERENCE_KEY_IV, "");
141
142 byte[] iv = Base64.decode(keyIV, Base64.NO_WRAP);
143 IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
144
145 cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
146
147 return true;
148 }
149
150 } catch (NoSuchAlgorithmException e) {
151 //
152 } catch (NoSuchPaddingException e) {
153 //
154 } catch (InvalidAlgorithmParameterException e) {
155 //
156 }
157
158 return false;
159 }
160
161 @RequiresApi(Build.VERSION_CODES.M)
162 private void deleteKeyPair() {
163 try {
164 KeyStore keyStore = KeyStore.getInstance(KEY_STORE);
165 keyStore.load(null);
166 keyStore.deleteEntry(KEY_ALIAS);
167 } catch (KeyStoreException e) {
168 //
169 } catch (CertificateException e) {
170 //
171 } catch (NoSuchAlgorithmException e) {
172 //
173 } catch (IOException e) {
174 //
175 }
176 }
177
178 @RequiresApi(Build.VERSION_CODES.M)
179 private boolean createKeyPair(boolean forceCreate) {
180
181 try {
182 keyStore = KeyStore.getInstance(KEY_STORE);
183 keyStore.load(null);
184
185 if (forceCreate) {
186 keyStore.deleteEntry(KEY_ALIAS);
187 }
188
189 if (!keyStore.containsAlias(KEY_ALIAS)) {
190 KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, KEY_STORE);
191 keyGenerator.init(
192 new KeyGenParameterSpec.Builder(KEY_ALIAS, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
193 .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
194 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
195 .setUserAuthenticationRequired(true)
196 .build()
197 );
198 keyGenerator.generateKey();
199 }
200
201 secretKey = (SecretKey) keyStore.getKey(KEY_ALIAS, null);
202
203 return true;
204
205 } catch (KeyStoreException e) {
206 //
207 } catch (CertificateException e) {
208 //
209 } catch (UnrecoverableKeyException e) {
210 //
211 } catch (InvalidAlgorithmParameterException e) {
212 //
213 } catch (NoSuchAlgorithmException e) {
214 //
215 } catch (NoSuchProviderException e) {
216 //
217 } catch (IOException e) {
218 //
219 }
220
221 return false;
222 }
223
224 @RequiresApi(Build.VERSION_CODES.M)
225 @Nullable
226 @Override
227 public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
228 Logger.d(TAG, "onCreateView");
229
230 View view = inflater.inflate(R.layout.dialog_fingerprint, container, false);
231
232 Bundle args = getArguments();
233 if (args != null) {
234 title = args.getString(TITLE);
235 subject = args.getString(SUBJECT);
236 mode = args.getInt(MODE);
237 }
238
239 titleTextView = view.findViewById(R.id.title_text);
240 titleTextView.setText(title);
241 errorTextView = view.findViewById(R.id.error_text);
242 cancelButton = view.findViewById(R.id.cancel_button);
243 cancelButton.setOnClickListener(new View.OnClickListener() {
244 @Override
245 public void onClick(View v) {
246 dismiss();
247 if (listener != null) {
248 listener.onCancel();
249 }
250 }
251 });
252
253 usePasswordButton = view.findViewById(R.id.use_password_button);
254 usePasswordButton.setOnClickListener(new View.OnClickListener() {
255 @Override
256 public void onClick(View v) {
257 dismiss();
258 deleteKeyPair();
259 if (listener != null) {
260 listener.onDisable();
261 }
262 }
263 });
264
265 if (createKeyPair(false)) {
266 try {
267
268 if (initCipher(getContext(), mode)) {
269 cancellationSignal = new CancellationSignal();
270 fingerprintManager = (FingerprintManager) getContext().getSystemService(Context.FINGERPRINT_SERVICE);
271 fingerprintManager.authenticate(
272 new FingerprintManager.CryptoObject(cipher),
273 cancellationSignal,
274 0,
275 new AuthenticationCallback(),
276 null
277 );
278 }
279
280 } catch (InvalidKeyException e) {
281 errorTextView.setText("Sidik jari tidak tersedia");
282 }
283 } else {
284 errorTextView.setText("Sidik jari tidak tersedia");
285 }
286
287 setCancelable(false);
288
289 return view;
290 }
291
292 @Override
293 public void onPause() {
294 if (cancellationSignal != null) {
295 Logger.d(TAG, "cancellationSignal.cancel()");
296 cancellationSignal.cancel();
297 cancellationSignal = null;
298 }
299 super.onPause();
300 }
301
302 public static boolean isFingerprintAvailable(Context context) {
303
304 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
305 return false;
306 }
307
308 KeyguardManager keyguardManager = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
309 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
310 if (!keyguardManager.isKeyguardSecure()) {
311 Logger.d(TAG, "Lock screen is not enabled");
312 return false;
313 }
314 }
315
316 if (ContextCompat.checkSelfPermission(context, Manifest.permission.USE_FINGERPRINT) != PackageManager.PERMISSION_GRANTED) {
317 Logger.d(TAG, "Missing permission USE_FINGERPRINT");
318 return false;
319 }
320
321 FingerprintManager fingerprintManager = (FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE);
322 if (fingerprintManager == null || !fingerprintManager.isHardwareDetected()) {
323 Logger.d(TAG, "Fingerprint support not available on this device");
324 return false;
325 }
326
327 if (!fingerprintManager.hasEnrolledFingerprints()) {
328 Logger.d(TAG, "No fingerprint has been enrolled on this device");
329 return false;
330 }
331
332 return true;
333 }
334}