Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- ...
- } else {
- ...
- }
- 05-31 14:35:50.924 11941-11941/com.example.app E/dalvikvm: Could not find class 'android.security.keystore.KeyGenParameterSpec$Builder', referenced from method com.example.app.ui.fragment.util.LoginFragment.createKeyPair
- 05-31 14:35:50.924 11941-11941/com.example.app W/dalvikvm: VFY: unable to resolve new-instance 263 (Landroid/security/keystore/KeyGenParameterSpec$Builder;) in Lcom/example/app/ui/fragment/util/LoginFragment;
- 05-31 14:35:50.924 11941-11941/com.example.app D/dalvikvm: VFY: replacing opcode 0x22 at 0x000c
- 05-31 14:35:50.924 11941-11941/com.example.app W/dalvikvm: VFY: unable to resolve exception class 265 (Landroid/security/keystore/KeyPermanentlyInvalidatedException;)
- 05-31 14:35:50.924 11941-11941/com.example.app W/dalvikvm: VFY: unable to find exception handler at addr 0x3f
- 05-31 14:35:50.924 11941-11941/com.example.app W/dalvikvm: VFY: rejected Lcom/example/app/ui/fragment/util/LoginFragment;.initializeCipher (I)Z
- 05-31 14:35:50.924 11941-11941/cp W/dalvikvm: VFY: rejecting opcode 0x0d at 0x003f
- 05-31 14:35:50.924 11941-11941/com.example.app W/dalvikvm: VFY: rejected Lcom/example/app/ui/fragment/util/LoginFragment;.initializeCipher (I)Z
- 05-31 14:35:50.924 11941-11941/com.example.app W/dalvikvm: Verifier rejected class Lcom/example/app/ui/fragment/util/LoginFragment;
- 05-31 14:35:50.924 11941-11941/com.example.app D/AndroidRuntime: Shutting down VM
- 05-31 14:35:50.924 11941-11941/com.example.app W/dalvikvm: threadid=1: thread exiting with uncaught exception (group=0x9cca9b20)
- 05-31 14:35:50.934 11941-11941/com.example.app E/AndroidRuntime: FATAL EXCEPTION: main
- Process: com.example.app, PID: 11941 java.lang.VerifyError: com/example/app/ui/fragment/util/LoginFragment
- at com.example.app.util.NetworkUtility.login(NetworkUtility.java:41)
- at com.example.app.ui.activity.AbstractNavActivity.onOptionsItemSelected(AbstractNavActivity.java:68)
- at android.app.Activity.onMenuItemSelected(Activity.java:2600)
- at android.support.v4.app.FragmentActivity.onMenuItemSelected(FragmentActivity.java:403)
- at android.support.v7.app.AppCompatActivity.onMenuItemSelected(AppCompatActivity.java:189)
- at android.support.v7.view.WindowCallbackWrapper.onMenuItemSelected(WindowCallbackWrapper.java:100)
- at android.support.v7.view.WindowCallbackWrapper.onMenuItemSelected(WindowCallbackWrapper.java:100)
- at android.support.v7.app.ToolbarActionBar$2.onMenuItemClick(ToolbarActionBar.java:69)
- at android.support.v7.widget.Toolbar$1.onMenuItemClick(Toolbar.java:169)
- at android.support.v7.widget.ActionMenuView$MenuBuilderCallback.onMenuItemSelected(ActionMenuView.java:760)
- at android.support.v7.view.menu.MenuBuilder.dispatchMenuItemSelected(MenuBuilder.java:811)
- at android.support.v7.view.menu.MenuItemImpl.invoke(MenuItemImpl.java:152)
- at android.support.v7.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:958)
- at android.support.v7.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:948)
- at android.support.v7.view.menu.MenuPopupHelper.onItemClick(MenuPopupHelper.java:191)
- at android.widget.AdapterView.performItemClick(AdapterView.java:299)
- at android.widget.AbsListView.performItemClick(AbsListView.java:1113)
- at android.widget.AbsListView$PerformClick.run(AbsListView.java:2904)
- at android.widget.AbsListView$3.run(AbsListView.java:3638)
- at android.os.Handler.handleCallback(Handler.java:733)
- at android.os.Handler.dispatchMessage(Handler.java:95)
- at android.os.Looper.loop(Looper.java:136)
- at android.app.ActivityThread.main(ActivityThread.java:5017)
- at java.lang.reflect.Method.invokeNative(Native Method)
- at java.lang.reflect.Method.invoke(Method.java:515)
- at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
- at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
- at dalvik.system.NativeStart.main(Native Method)
- public static void login(FragmentManager manager) {
- manager.beginTransAction().add(LoginFragment.newInstance(), null).commit();
- }
- public class LoginFragment extends DialogFragment
- implements TextView.OnEditorActionListener, FingerprintCallback.Callback {
- ...
- public static LoginFragment newInstance() {
- return newInstance(null);
- }
- public static LoginFragment newInstance(Intent intent) {
- LoginFragment fragment = new LoginFragment();
- Bundle args = new Bundle();
- args.putParcelable(EXTRA_INTENT, intent);
- fragment.setArguments(args);
- return fragment;
- }
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- Injector.getContextComponent().inject(this);
- setStyle(STYLE_NO_TITLE, R.style.DialogTheme);
- setRetainInstance(true);
- setCancelable(false);
- mSaveUsernamePreference = mPreferences.getBoolean(getString(R.string.key_auth_username_retain));
- mUseFingerprintPreference = mPreferences.getBoolean(getString(R.string.key_auth_fingerprint));
- mUsernamePreference = mPreferences.getString(getString(R.string.key_auth_username));
- mPasswordPreference = mPreferences.getString(getString(R.string.key_auth_password));
- }
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- View view = inflater.inflate(R.layout.dialog_login_container, container, false);
- ButterKnife.bind(this, view);
- mPasswordView.setOnEditorActionListener(this);
- if(!mFingerprintManager.isHardwareDetected()) {
- mUseFingerprintToggle.setVisibility(View.GONE);
- } else {
- mGenerated = initializeKeyPair(false);
- }
- if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- setStage(isFingerprintAvailable() ? Stage.FINGERPRINT : Stage.CREDENTIALS);
- } else {
- setStage(Stage.CREDENTIALS);
- }
- return view;
- }
- @Override
- public void onResume() {
- super.onResume();
- ...
- if(mStage == Stage.FINGERPRINT && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- startListening(initializeCipher(Cipher.DECRYPT_MODE));
- }
- }
- @Override
- public void onPause() {
- super.onPause();
- stopListening();
- }
- ...
- @Override
- public void onAuthenticationSucceeded(FingerprintManagerCompat.AuthenticationResult result) {
- Timber.i("Fingerprint succeeded");
- showFingerprintSuccess();
- mSubscriptions.add(
- mGenerated.subscribeOn(Schedulers.newThread())
- .observeOn(AndroidSchedulers.mainThread())
- .doOnCompleted(() -> {
- try {
- mUsername = mUsernamePreference.get();
- mPassword = decryptPassword(result.getCryptoObject().getCipher());
- initLoginAttempt();
- } catch (IllegalBlockSizeException | BadPaddingException exception) {
- Timber.e(exception, "Failed to decrypt password");
- }
- }).subscribe());
- }
- @Override
- public void onAuthenticationHelp(int messageId, CharSequence message) {
- Timber.i("Fingerprint help id: " + messageId + " message: " + message);
- showFingerprintError(message);
- }
- @Override
- public void onAuthenticationError(int messageId, CharSequence message) {
- Timber.i("Fingerprint error id: " + messageId + " message: " + message);
- if(messageId != 5) {
- showFingerprintError(message);
- }
- }
- @Override
- public void onAuthenticationFailed() {
- Timber.i("Fingerprint failed");
- showFingerprintError(getResources().getString(R.string.msg_fingerprint_error_unknown));
- }
- @OnClick(R.id.button_cancel)
- public void onCancel() {
- dismiss();
- }
- @OnClick(R.id.button_continue)
- public void onContinue() {
- switch (mStage) {
- case CREDENTIALS:
- mUsername = mUsernameView.getText().toString();
- mPassword = mPasswordView.getText().toString();
- initLoginAttempt();
- break;
- case FINGERPRINT:
- setStage(Stage.CREDENTIALS);
- break;
- }
- }
- private void showFingerprintSuccess() {
- int colorAccent = ThemeUtil.getColorAttribute(getContext(), android.R.attr.colorAccent);
- mFingerprintIcon.setImageResource(R.drawable.ic_done_white_24dp);
- mFingerprintIcon.setCircleColor(colorAccent);
- mFingerprintStatus.setText(R.string.msg_fingerprint_success);
- mFingerprintStatus.setTextColor(colorAccent);
- }
- private void showFingerprintError(CharSequence message) {
- int colorError = ContextCompat.getColor(getContext(), R.color.material_deep_orange_600);
- mFingerprintIcon.setImageResource(R.drawable.ic_priority_high_white_24dp);
- mFingerprintIcon.setCircleColor(colorError);
- mFingerprintStatus.setText(message);
- mFingerprintStatus.setTextColor(colorError);
- resetFingerprintStatus();
- }
- private void resetFingerprintStatus() {
- mSubscriptions.add(Observable.timer(1600, TimeUnit.MILLISECONDS)
- .subscribeOn(Schedulers.newThread())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(finished -> {
- mFingerprintIcon.setImageResource(R.drawable.ic_fingerprint_white_24dp);
- mFingerprintIcon.setCircleColor(ContextCompat
- .getColor(getContext(), R.color.material_blue_gray_500));
- mFingerprintStatus.setText(R.string.msg_fingerprint_input);
- mFingerprintStatus.setTextColor(ThemeUtil
- .getColorAttribute(getContext(), android.R.attr.textColorHint));
- }));
- }
- private void onSaveUsernameChanged(boolean checked) {
- if(!checked) {
- mUseFingerprintToggle.setChecked(false);
- }
- }
- private void onUseFingerprintChanged(boolean checked) {
- if(checked) {
- mSaveUsernameToggle.setChecked(true);
- if(!mFingerprintManager.hasEnrolledFingerprints()) {
- displaySettingsDialog();
- mUseFingerprintToggle.setChecked(false);
- }
- }
- }
- public void setStage(Stage stage) {
- switch (stage) {
- case CREDENTIALS:
- Timber.d("Set stage Credentials");
- mPositiveButton.setText(R.string.btn_login);
- mFingerprintContent.setVisibility(View.GONE);
- mCredentialContent.setVisibility(View.VISIBLE);
- setForm();
- break;
- case FINGERPRINT:
- mPositiveButton.setText(R.string.btn_password);
- mCredentialContent.setVisibility(View.GONE);
- mFingerprintContent.setVisibility(View.VISIBLE);
- break;
- } mStage = stage;
- }
- private void startListening(boolean cipher) {
- Timber.v("Start listening for fingerprint input");
- mCancellationSignal = new CancellationSignal();
- if(cipher) {
- mFingerprintManager.authenticate(new FingerprintManagerCompat.CryptoObject(mCipher),
- 0, mCancellationSignal, new FingerprintCallback(this), null);
- } else {
- setStage(Stage.CREDENTIALS);
- }
- }
- private void stopListening() {
- if(mCancellationSignal != null) {
- mCancellationSignal.cancel();
- mCancellationSignal = null;
- }
- }
- private void setForm() {
- if(mSaveUsernamePreference.isSet() && mSaveUsernamePreference.get()
- && mUsernamePreference.isSet()) {
- mUsernameView.setText(mUsernamePreference.get());
- mUsernameView.setSelectAllOnFocus(true);
- mPasswordView.requestFocus();
- } else {
- mUsernameView.requestFocus();
- }
- }
- public void initLoginAttempt() {
- mProgressBar.setVisibility(View.VISIBLE);
- mAuthenticationService.getLoginForm().subscribeOn(Schedulers.newThread())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(this::onLoginFormResponse, this::onError);
- }
- private void onLoginFormResponse(ResponseBody response) {
- try {
- attemptLogin(LoginForm.parse(response.string()));
- } catch (IOException exception) {
- Timber.w(exception, "Failed to parse login form");
- }
- }
- private void attemptLogin(LoginForm loginForm) {
- mAuthenticationService
- .login(loginForm.getLoginTicket(), loginForm.getExecution(), loginForm.getEventIdentifier(),
- mUsername, mPassword, loginForm.getSubmitValue())
- .subscribeOn(Schedulers.newThread())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(this::onLoginResponse, this::onError);
- }
- public void onLoginResponse(ResponseBody response) {
- Timber.d("LOGIN RESPONSE");
- try {
- Timber.d(response.string());
- } catch (IOException exception) {
- Timber.w(exception, "Failed to retrieve attemptLogin response");
- }
- mSubscriptions.add(NetworkUtility.getAuthentication()
- .subscribeOn(Schedulers.newThread())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(this::onAuthenticationChanged, this::onError));
- }
- public void onAuthenticationChanged(Boolean authenticated) {
- if(authenticated) {
- Timber.d("Authentication success");
- if(mStage == Stage.CREDENTIALS) {
- if (mSaveUsernameToggle.isChecked()) {
- storeUsername();
- } else {
- clearUsername();
- }
- if (mUseFingerprintToggle.isChecked()) {
- mGenerated = initializeKeyPair(true);
- storePassword();
- } else {
- clearPassword();
- finishIntent();
- }
- } else {
- finishIntent();
- }
- } else {
- Timber.d("Authentication failed");
- setStage(Stage.CREDENTIALS);
- mCaptionView.setTextColor(ContextCompat.getColor(getContext(), R.color.material_deep_orange_600));
- mCaptionView.setText(getString(R.string.msg_login_failed));
- mPasswordView.setText("");
- }
- }
- private void finishIntent() {
- mProgressBar.setVisibility(View.INVISIBLE);
- Intent intent = getArguments().getParcelable(EXTRA_INTENT);
- if(intent != null) {
- startActivity(intent);
- } dismiss();
- }
- private void onError(Throwable throwable) {
- Timber.w(throwable, "Login attempt failed");
- mProgressBar.setVisibility(View.INVISIBLE);
- mCaptionView.setTextColor(ContextCompat.getColor(getContext(), R.color.material_deep_orange_600));
- mCaptionView.setText("Login attempt failednPlease check your internet connection and try again");
- mPasswordView.setText("");
- }
- private void storeUsername() {
- String username = mUsernameView.getText().toString();
- mUsernamePreference.set(username);
- if(mPreferences.getBoolean(getString(R.string.key_auth_push), false).get()) {
- UAirship.shared().getPushManager().getNamedUser().setId(username);
- }
- }
- private void clearUsername() {
- UAirship.shared().getPushManager().getNamedUser().setId(null);
- mUsernamePreference.delete();
- }
- private void storePassword() {
- Timber.d("STORE PASSWORD");
- mSubscriptions.add(mGenerated.subscribeOn(Schedulers.newThread())
- .observeOn(AndroidSchedulers.mainThread())
- .doOnCompleted(() -> {
- try {
- Timber.d("Store password");
- initializeCipher(Cipher.ENCRYPT_MODE);
- String password = mPasswordView.getText().toString();
- byte[] bytes = password.getBytes();
- byte[] encrypted = mCipher.doFinal(bytes);
- String encoded = Base64.encodeToString(encrypted, Base64.NO_WRAP);
- mPasswordPreference.set(encoded);
- finishIntent();
- } catch (IllegalBlockSizeException | BadPaddingException exception) {
- Timber.e(exception, "Failed to encrypt password");
- }
- }).subscribe());
- }
- private String decryptPassword(Cipher cipher) throws IllegalBlockSizeException, BadPaddingException {
- String encoded = mPasswordPreference.get();
- Timber.d("ENCODED STRING " + encoded);
- byte[] encrypted = Base64.decode(encoded, Base64.NO_WRAP);
- byte[] bytes = cipher.doFinal(encrypted);
- return new String(bytes);
- }
- private void clearPassword() {
- mPasswordPreference.delete();
- }
- private boolean isFingerprintAvailable() {
- return mUseFingerprintPreference.isSet() && mUseFingerprintPreference.get()
- && mFingerprintManager.hasEnrolledFingerprints()
- && mSaveUsernamePreference.isSet()
- && mPasswordPreference.isSet();
- }
- private void displaySettingsDialog() {
- new AlertDialog.Builder(getContext())
- .setTitle(R.string.title_dialog_secure_lock)
- .setMessage(R.string.msg_fingerprint_unavailable)
- .setPositiveButton(R.string.btn_settings, (dialog, which) -> {
- startActivity(new Intent(android.provider.Settings.ACTION_SECURITY_SETTINGS));
- dialog.dismiss();
- }).setNegativeButton(R.string.btn_cancel, (dialog, which) -> {
- dialog.dismiss();
- }).create().show();
- }
- @TargetApi(Build.VERSION_CODES.M)
- private boolean initializeCipher(int opmode) {
- try {
- mKeyStore.load(null);
- /**
- * A known bug in the Android 6.0 (API Level 23) implementation of Bouncy Castle
- * RSA OAEP causes the cipher to default to an SHA-1 certificate, making the SHA-256
- * certificate of the public key incompatible
- * To work around this issue, explicitly provide a new OAEP specification upon
- * initialization
- * @see <a href="https://code.google.com/p/android/issues/detail?id=197719">Issue 197719</a>
- */
- AlgorithmParameterSpec spec = generateOAEPParameterSpec();
- Key key;
- if(opmode == Cipher.ENCRYPT_MODE) {
- Key publicKey = mKeyStore.getCertificate(CIPHER_KEY_ALIAS).getPublicKey();
- /**
- * A known bug in Android 6.0 (API Level 23) causes user authentication-related
- * authorizations to be enforced even for public keys
- * To work around this issue, extract the public key material to use outside of
- * the Android Keystore
- * @see <a href="http://developer.android.com/reference/android/security/keystore/KeyGenParameterSpec.html">KeyGenParameterSpec Known Issues</a>
- */
- key = KeyFactory.getInstance(publicKey.getAlgorithm())
- .generatePublic(new X509EncodedKeySpec(publicKey.getEncoded()));
- } else {
- key = mKeyStore.getKey(CIPHER_KEY_ALIAS, null);
- }
- mCipher.init(opmode, key, spec);
- return true;
- } catch (KeyPermanentlyInvalidatedException exception) {
- Timber.w(exception, "Failed to initialize Cipher");
- handleKeyPermanentlyInvalidated();
- return false;
- } catch (IOException | KeyStoreException | UnrecoverableEntryException
- | InvalidKeySpecException | CertificateException | InvalidKeyException
- | NoSuchAlgorithmException | InvalidAlgorithmParameterException exception) {
- throw new RuntimeException("Failed to initialize Cipher", exception);
- }
- }
- private OAEPParameterSpec generateOAEPParameterSpec() {
- return new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA1, PSource.PSpecified.DEFAULT);
- }
- private void handleKeyPermanentlyInvalidated() {
- mCaptionView.setText(getString(R.string.msg_fingerprint_invalidated));
- mGenerated = initializeKeyPair(true);
- clearPassword();
- }
- private Observable<KeyPair> initializeKeyPair(boolean generate) {
- return Observable.create(subscriber -> {
- try {
- mKeyStore.load(null);
- if(!generate || mKeyStore.containsAlias(CIPHER_KEY_ALIAS)) {
- PublicKey publicKey = mKeyStore.getCertificate(CIPHER_KEY_ALIAS).getPublicKey();
- PrivateKey privateKey = (PrivateKey) mKeyStore.getKey(CIPHER_KEY_ALIAS, null);
- subscriber.onNext(new KeyPair(publicKey, privateKey));
- } else {
- subscriber.onNext(createKeyPair());
- }
- subscriber.onCompleted();
- } catch (IOException | KeyStoreException | UnrecoverableKeyException
- | CertificateException | NoSuchAlgorithmException
- | InvalidAlgorithmParameterException exception) {
- Timber.e(exception, "Failed to generate key pair");
- subscriber.onError(exception);
- }
- });
- }
- @TargetApi(Build.VERSION_CODES.M)
- private KeyPair createKeyPair() throws InvalidAlgorithmParameterException {
- // Set the alias of the entry in Android KeyStore where the key will appear
- // and the constrains (purposes) in the constructor of the Builder
- Timber.d("Initialize key pair");
- mKeyPairGenerator.initialize(
- new KeyGenParameterSpec.Builder(CIPHER_KEY_ALIAS, KeyProperties.PURPOSE_DECRYPT)
- .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
- .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP)
- .setUserAuthenticationRequired(true)
- .build());
- return mKeyPairGenerator.generateKeyPair();
- }
- }
- catch (KeyPermanentlyInvalidatedException exception) {
- Timber.w(exception, "A new fingerprint was added to the device");
- handleKeyPermanentlyInvalidated();
- return false;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement