예제 #1
0
  private void shareKey(Uri dataUri, boolean fingerprintOnly) {
    String content;
    if (fingerprintOnly) {
      byte[] fingerprintBlob = ProviderHelper.getFingerprint(this, dataUri);
      String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob, false);

      content = Constants.FINGERPRINT_SCHEME + ":" + fingerprint;
    } else {
      // get public keyring as ascii armored string
      long masterKeyId = ProviderHelper.getMasterKeyId(this, dataUri);
      ArrayList<String> keyringArmored =
          ProviderHelper.getKeyRingsAsArmoredString(this, dataUri, new long[] {masterKeyId});

      content = keyringArmored.get(0);

      // Android will fail with android.os.TransactionTooLargeException if key is too big
      // see http://www.lonestarprod.com/?p=34
      if (content.length() >= 86389) {
        Toast.makeText(getApplicationContext(), R.string.key_too_big_for_sharing, Toast.LENGTH_LONG)
            .show();
        return;
      }
    }

    // let user choose application
    Intent sendIntent = new Intent(Intent.ACTION_SEND);
    sendIntent.putExtra(Intent.EXTRA_TEXT, content);
    sendIntent.setType("text/plain");
    startActivity(
        Intent.createChooser(sendIntent, getResources().getText(R.string.action_share_key_with)));
  }
예제 #2
0
  private void copyToClipboard(Uri dataUri) {
    // get public keyring as ascii armored string
    long masterKeyId = ProviderHelper.getMasterKeyId(this, dataUri);
    ArrayList<String> keyringArmored =
        ProviderHelper.getKeyRingsAsArmoredString(this, dataUri, new long[] {masterKeyId});

    ClipboardReflection.copyToClipboard(this, keyringArmored.get(0));
    Toast.makeText(getApplicationContext(), R.string.key_copied_to_clipboard, Toast.LENGTH_LONG)
        .show();
  }
  @Before
  public void setUp() throws Exception {
    ProviderHelper providerHelper = new ProviderHelper(RuntimeEnvironment.application);

    // don't log verbosely here, we're not here to test imports
    ShadowLog.stream = oldShadowStream;

    providerHelper.savePublicKeyRing(
        mStaticRing.extractPublicKeyRing(), new ProgressScaler(), null);

    // ok NOW log verbosely!
    ShadowLog.stream = System.out;
  }
예제 #4
0
  public static String getFingerPrint(Context context, long keyId) {
    PGPPublicKey key = ProviderHelper.getPGPPublicKeyByKeyId(context, keyId);
    // if it is no public key get it from your own keys...
    if (key == null) {
      PGPSecretKey secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, keyId);
      if (secretKey == null) {
        Log.e(Constants.TAG, "Key could not be found!");
        return null;
      }
      key = secretKey.getPublicKey();
    }

    return convertFingerprintToHex(key.getFingerprint(), true);
  }
  /**
   * If an Intent gives a signatureMasterKeyId and/or encryptionMasterKeyIds, preselect those!
   *
   * @param preselectedSignatureKeyId
   * @param preselectedEncryptionKeyIds
   */
  private void preselectKeys(
      long preselectedSignatureKeyId,
      long[] preselectedEncryptionKeyIds,
      ProviderHelper providerHelper) {
    if (preselectedSignatureKeyId != 0) {
      // TODO: don't use bouncy castle objects!
      try {
        PGPSecretKeyRing keyRing =
            providerHelper.getPGPSecretKeyRingWithKeyId(preselectedSignatureKeyId);

        PGPSecretKey masterKey = keyRing.getSecretKey();
        if (masterKey != null) {
          PGPSecretKey signKey = PgpKeyHelper.getFirstSigningSubkey(keyRing);
          if (signKey != null) {
            setSignatureKeyId(masterKey.getKeyID());
          }
        }
      } catch (ProviderHelper.NotFoundException e) {
        Log.e(Constants.TAG, "key not found!", e);
      }
    }

    if (preselectedEncryptionKeyIds != null) {
      Vector<Long> goodIds = new Vector<Long>();
      for (int i = 0; i < preselectedEncryptionKeyIds.length; ++i) {
        // TODO One query per selected key?! wtf
        try {
          long id =
              providerHelper.getMasterKeyId(
                  KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(
                      Long.toString(preselectedEncryptionKeyIds[i])));
          goodIds.add(id);
        } catch (ProviderHelper.NotFoundException e) {
          Log.e(Constants.TAG, "key not found!", e);
        }
      }
      if (goodIds.size() > 0) {
        long[] keyIds = new long[goodIds.size()];
        for (int i = 0; i < goodIds.size(); ++i) {
          keyIds[i] = goodIds.get(i);
        }
        setEncryptionKeyIds(keyIds);
      }
    }
  }
  private void encryptToContact(Uri dataUri) {
    long keyId = ProviderHelper.getMasterKeyId(getActivity(), dataUri);

    long[] encryptionKeyIds = new long[] {keyId};
    Intent intent = new Intent(getActivity(), EncryptActivity.class);
    intent.setAction(EncryptActivity.ACTION_ENCRYPT);
    intent.putExtra(EncryptActivity.EXTRA_ENCRYPTION_KEY_IDS, encryptionKeyIds);
    // used instead of startActivity set actionbar based on callingPackage
    startActivityForResult(intent, 0);
  }
예제 #7
0
 @Override
 public Passphrase getCachedPassphrase(long subKeyId) throws NoSecretKeyException {
   try {
     if (subKeyId != key.symmetric) {
       long masterKeyId = mProviderHelper.getMasterKeyId(subKeyId);
       return getCachedPassphrase(masterKeyId, subKeyId);
     }
     return getCachedPassphrase(key.symmetric, key.symmetric);
   } catch (NotFoundException e) {
     throw new PassphraseCacheInterface.NoSecretKeyException();
   }
 }
예제 #8
0
 public static PGPSecretKey getSigningKey(Context context, long masterKeyId) {
   PGPSecretKeyRing keyRing =
       ProviderHelper.getPGPSecretKeyRingByMasterKeyId(context, masterKeyId);
   if (keyRing == null) {
     return null;
   }
   Vector<PGPSecretKey> signingKeys = getUsableSigningKeys(keyRing);
   if (signingKeys.size() == 0) {
     return null;
   }
   return signingKeys.get(0);
 }
예제 #9
0
 public static PGPPublicKey getEncryptPublicKey(Context context, long masterKeyId) {
   PGPPublicKeyRing keyRing =
       ProviderHelper.getPGPPublicKeyRingByMasterKeyId(context, masterKeyId);
   if (keyRing == null) {
     Log.e(Constants.TAG, "keyRing is null!");
     return null;
   }
   Vector<PGPPublicKey> encryptKeys = getUsableEncryptKeys(keyRing);
   if (encryptKeys.size() == 0) {
     Log.e(Constants.TAG, "encryptKeys is null!");
     return null;
   }
   return encryptKeys.get(0);
 }
예제 #10
0
  private void updateFromKeyserver(Uri dataUri) {
    long updateKeyId = ProviderHelper.getMasterKeyId(ViewKeyActivity.this, dataUri);

    if (updateKeyId == 0) {
      Log.e(Constants.TAG, "this shouldn't happen. KeyId == 0!");
      return;
    }

    Intent queryIntent = new Intent(this, ImportKeysActivity.class);
    queryIntent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER);
    queryIntent.putExtra(ImportKeysActivity.EXTRA_KEY_ID, updateKeyId);

    // TODO: lookup with onactivityresult!
    startActivityForResult(queryIntent, RESULT_CODE_LOOKUP_KEY);
  }
예제 #11
0
  private void updateFromKeyserver(Uri dataUri, ProviderHelper providerHelper)
      throws ProviderHelper.NotFoundException {
    byte[] blob =
        (byte[])
            providerHelper.getGenericData(
                KeychainContract.KeyRings.buildUnifiedKeyRingUri(dataUri),
                KeychainContract.Keys.FINGERPRINT,
                ProviderHelper.FIELD_TYPE_BLOB);
    String fingerprint = PgpKeyHelper.convertFingerprintToHex(blob);

    Intent queryIntent = new Intent(getActivity(), ImportKeysActivity.class);
    queryIntent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_RESULT);
    queryIntent.putExtra(ImportKeysActivity.EXTRA_FINGERPRINT, fingerprint);

    startActivityForResult(queryIntent, 0);
  }
  public void drawPublicKey() {
    PGPPublicKeyRing pubring = ProviderHelper.getPGPPublicKeyRingByMasterKeyId(this, mPubKeyId);

    if (pubring != null) {
      PGPPublicKey key = PgpKeyHelper.getMasterKey(pubring);
      String masterKeyIdHex = PgpKeyHelper.convertKeyIdToHex(mPubKeyId);

      // get relevant UI elements
      TextView keyIdHex = (TextView) findViewById(R.id.public_key_master_key_hex);
      TextView keyUserId = (TextView) findViewById(R.id.public_key_user_id);
      TextView keyUserIdRest = (TextView) findViewById(R.id.public_key_user_id_rest);

      if (key != null) {
        String userId = PgpKeyHelper.getMainUserIdSafe(this, key);

        String[] userIdSplit = PgpKeyHelper.splitUserId(userId);
        String userName, userEmail;

        if (userIdSplit[0] != null) {
          userName = userIdSplit[0];
        } else {
          userName = getResources().getString(R.string.user_id_no_name);
        }

        if (userIdSplit[1] != null) {
          userEmail = userIdSplit[1];
        } else {
          userEmail = getResources().getString(R.string.error_user_id_no_email);
        }

        keyIdHex.setText(masterKeyIdHex);

        keyUserId.setText(userName);
        keyUserIdRest.setText(userEmail);
        keyUserId.setVisibility(View.VISIBLE);
        keyUserIdRest.setVisibility(View.VISIBLE);
      } else {
        Log.e(Constants.TAG, "this shouldn't happen. key == 0!");
        finish();
        return;
      }
    } else {
      Log.e(Constants.TAG, "this shouldn't happen. pubring == 0!");
      finish();
      return;
    }
  }
  public void initValid(CanonicalizedPublicKey signingKey) {
    setSignatureAvailable(true);
    setKnownKey(true);

    CanonicalizedKeyRing signingRing = signingKey.getKeyRing();

    // from RING
    setKeyId(signingRing.getMasterKeyId());
    try {
      setPrimaryUserId(signingRing.getPrimaryUserIdWithFallback());
    } catch (PgpKeyNotFoundException e) {
      Log.d(
          Constants.TAG,
          "No primary user id in keyring with master key id " + signingRing.getMasterKeyId());
    }
    setSignatureKeyCertified(signingRing.getVerified() > 0);

    try {
      ArrayList<String> allUserIds = signingRing.getUnorderedUserIds();
      ArrayList<String> confirmedUserIds =
          mProviderHelper.getConfirmedUserIds(signingRing.getMasterKeyId());
      setUserIds(allUserIds, confirmedUserIds);

      if (mSenderAddress != null) {
        if (userIdListContainsAddress(mSenderAddress, confirmedUserIds)) {
          mSenderStatusResult = SenderStatusResult.USER_ID_CONFIRMED;
        } else if (userIdListContainsAddress(mSenderAddress, allUserIds)) {
          mSenderStatusResult = SenderStatusResult.USER_ID_UNCONFIRMED;
        } else {
          mSenderStatusResult = SenderStatusResult.USER_ID_MISSING;
        }
      } else {
        mSenderStatusResult = SenderStatusResult.UNKNOWN;
      }

    } catch (NotFoundException e) {
      throw new IllegalStateException("Key didn't exist anymore for user id query!", e);
    }

    // either master key is expired/revoked or this specific subkey is expired/revoked
    setKeyExpired(signingRing.isExpired() || signingKey.isExpired());
    setKeyRevoked(signingRing.isRevoked() || signingKey.isRevoked());
  }
  private void updateView() {
    if (mEncryptionKeyIds == null || mEncryptionKeyIds.length == 0) {
      mSelectKeysButton.setText(getString(R.string.select_keys_button_default));
    } else {
      mSelectKeysButton.setText(
          getResources()
              .getQuantityString(
                  R.plurals.select_keys_button,
                  mEncryptionKeyIds.length,
                  mEncryptionKeyIds.length));
    }

    if (mSecretKeyId == Constants.key.none) {
      mSign.setChecked(false);
      mMainUserId.setText("");
      mMainUserIdRest.setText("");
    } else {
      // See if we can get a user_id from a unified query
      String[] userId;
      try {
        String userIdResult =
            (String)
                mProviderHelper.getUnifiedData(
                    mSecretKeyId, KeyRings.USER_ID, ProviderHelper.FIELD_TYPE_STRING);
        userId = PgpKeyHelper.splitUserId(userIdResult);
      } catch (ProviderHelper.NotFoundException e) {
        userId = null;
      }
      if (userId != null && userId[0] != null) {
        mMainUserId.setText(userId[0]);
      } else {
        mMainUserId.setText(getResources().getString(R.string.user_id_no_name));
      }
      if (userId != null && userId[1] != null) {
        mMainUserIdRest.setText(userId[1]);
      } else {
        mMainUserIdRest.setText("");
      }
      mSign.setChecked(true);
    }
  }
  /** handles the UI bits of the signing process on the UI thread */
  private void initiateSigning() {
    PGPPublicKeyRing pubring = ProviderHelper.getPGPPublicKeyRingByMasterKeyId(this, mPubKeyId);
    if (pubring != null) {
      // if we have already signed this key, dont bother doing it again
      boolean alreadySigned = false;

      @SuppressWarnings("unchecked")
      Iterator<PGPSignature> itr = pubring.getPublicKey(mPubKeyId).getSignatures();
      while (itr.hasNext()) {
        PGPSignature sig = itr.next();
        if (sig.getKeyID() == mMasterKeyId) {
          alreadySigned = true;
          break;
        }
      }

      if (!alreadySigned) {
        /*
         * get the user's passphrase for this key (if required)
         */
        String passphrase = PassphraseCacheService.getCachedPassphrase(this, mMasterKeyId);
        if (passphrase == null) {
          showPassphraseDialog(mMasterKeyId);
          // bail out; need to wait until the user has entered the passphrase before trying again
          return;
        } else {
          startSigning();
        }
      } else {
        Toast.makeText(this, R.string.key_has_already_been_signed, Toast.LENGTH_SHORT).show();

        setResult(RESULT_CANCELED);
        finish();
      }
    }
  }
  private void loadData(Uri dataUri) {
    if (dataUri.equals(mDataUri)) {
      Log.d(Constants.TAG, "Same URI, no need to load the data again!");
      return;
    }

    mDataUri = dataUri;

    Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString());

    { // label whether secret key is available, and edit button if it is
      final long masterKeyId = ProviderHelper.getMasterKeyId(getActivity(), mDataUri);
      if (ProviderHelper.hasSecretKeyByMasterKeyId(getActivity(), masterKeyId)) {
        // set this attribute. this is a LITTLE unclean, but we have the info available
        // right here, so why not.
        mSecretKey.setTextColor(getResources().getColor(R.color.emphasis));
        mSecretKey.setText(R.string.secret_key_yes);

        // certify button
        // TODO this button MIGHT be useful if the user wants to
        // certify a private key with another...
        // mActionCertify.setVisibility(View.GONE);

        // edit button
        mActionEdit.setVisibility(View.VISIBLE);
        mActionEdit.setOnClickListener(
            new View.OnClickListener() {
              public void onClick(View view) {
                Intent editIntent = new Intent(getActivity(), EditKeyActivity.class);
                editIntent.setData(
                    KeychainContract.KeyRings.buildSecretKeyRingsByMasterKeyIdUri(
                        Long.toString(masterKeyId)));
                editIntent.setAction(EditKeyActivity.ACTION_EDIT_KEY);
                startActivityForResult(editIntent, 0);
              }
            });
      } else {
        mSecretKey.setTextColor(Color.BLACK);
        mSecretKey.setText(getResources().getString(R.string.secret_key_no));

        // certify button
        mActionCertify.setVisibility(View.VISIBLE);
        // edit button
        mActionEdit.setVisibility(View.GONE);
      }

      // TODO see todo note above, doing this here for now
      mActionCertify.setOnClickListener(
          new View.OnClickListener() {
            public void onClick(View view) {
              certifyKey(
                  KeychainContract.KeyRings.buildPublicKeyRingsByMasterKeyIdUri(
                      Long.toString(masterKeyId)));
            }
          });
    }

    mActionEncrypt.setOnClickListener(
        new View.OnClickListener() {

          @Override
          public void onClick(View v) {
            encryptToContact(mDataUri);
          }
        });

    mUserIdsAdapter = new ViewKeyUserIdsAdapter(getActivity(), null, 0);
    mUserIds.setAdapter(mUserIdsAdapter);

    mKeysAdapter = new ViewKeyKeysAdapter(getActivity(), null, 0);
    mKeys.setAdapter(mKeysAdapter);

    // Prepare the loaders. Either re-connect with an existing ones,
    // or start new ones.
    getActivity().getSupportLoaderManager().initLoader(LOADER_ID_KEYRING, null, this);
    getActivity().getSupportLoaderManager().initLoader(LOADER_ID_USER_IDS, null, this);
    getActivity().getSupportLoaderManager().initLoader(LOADER_ID_KEYS, null, this);
  }
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.certify_key_activity);

    final ActionBar actionBar = getSupportActionBar();
    actionBar.setDisplayShowTitleEnabled(true);
    actionBar.setDisplayHomeAsUpEnabled(false);
    actionBar.setHomeButtonEnabled(false);

    mSelectKeyFragment =
        (SelectSecretKeyLayoutFragment)
            getSupportFragmentManager().findFragmentById(R.id.sign_key_select_key_fragment);
    mSelectKeyFragment.setCallback(this);
    mSelectKeyFragment.setFilterCertify(true);

    mSelectKeyserverSpinner = (Spinner) findViewById(R.id.sign_key_keyserver);
    ArrayAdapter<String> adapter =
        new ArrayAdapter<String>(
            this,
            android.R.layout.simple_spinner_item,
            Preferences.getPreferences(this).getKeyServers());
    adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    mSelectKeyserverSpinner.setAdapter(adapter);

    mUploadKeyCheckbox = (CheckBox) findViewById(R.id.sign_key_upload_checkbox);
    if (!mUploadKeyCheckbox.isChecked()) {
      mSelectKeyserverSpinner.setEnabled(false);
    } else {
      mSelectKeyserverSpinner.setEnabled(true);
    }

    mUploadKeyCheckbox.setOnCheckedChangeListener(
        new OnCheckedChangeListener() {

          @Override
          public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            if (!isChecked) {
              mSelectKeyserverSpinner.setEnabled(false);
            } else {
              mSelectKeyserverSpinner.setEnabled(true);
            }
          }
        });

    mSignButton = (BootstrapButton) findViewById(R.id.sign_key_sign_button);
    mSignButton.setOnClickListener(
        new OnClickListener() {

          @Override
          public void onClick(View v) {
            if (mPubKeyId != 0) {
              if (mMasterKeyId == 0) {
                mSelectKeyFragment.setError(getString(R.string.select_key_to_sign));
              } else {
                initiateSigning();
              }
            }
          }
        });

    mDataUri = getIntent().getData();
    if (mDataUri == null) {
      Log.e(Constants.TAG, "Intent data missing. Should be Uri of key!");
      finish();
      return;
    }

    PGPPublicKeyRing signKey = (PGPPublicKeyRing) ProviderHelper.getPGPKeyRing(this, mDataUri);

    if (signKey != null) {
      mPubKeyId = PgpKeyHelper.getMasterKey(signKey).getKeyID();
      drawPublicKey();
    }
    if (mPubKeyId == 0) {
      Log.e(Constants.TAG, "this shouldn't happen. KeyId == 0!");
      finish();
      return;
    }
  }
    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
      final Activity activity = getActivity();

      ContextThemeWrapper theme = ThemeChanger.getDialogThemeWrapper(activity);

      mRequiredInput = getArguments().getParcelable(EXTRA_REQUIRED_INPUT);

      CustomAlertDialogBuilder alert = new CustomAlertDialogBuilder(theme);

      // No title, see http://www.google.com/design/spec/components/dialogs.html#dialogs-alerts
      // alert.setTitle()

      if (mRequiredInput.mType == RequiredInputType.BACKUP_CODE) {
        LayoutInflater inflater = LayoutInflater.from(theme);
        View view = inflater.inflate(R.layout.passphrase_dialog_backup_code, null);
        alert.setView(view);

        mBackupCodeEditText = new EditText[4];
        mBackupCodeEditText[0] = (EditText) view.findViewById(R.id.backup_code_1);
        mBackupCodeEditText[1] = (EditText) view.findViewById(R.id.backup_code_2);
        mBackupCodeEditText[2] = (EditText) view.findViewById(R.id.backup_code_3);
        mBackupCodeEditText[3] = (EditText) view.findViewById(R.id.backup_code_4);
        setupEditTextFocusNext(mBackupCodeEditText);

        AlertDialog dialog = alert.create();
        dialog.setButton(
            DialogInterface.BUTTON_POSITIVE,
            activity.getString(R.string.btn_unlock),
            (DialogInterface.OnClickListener) null);
        return dialog;
      }

      long subKeyId = mRequiredInput.getSubKeyId();

      LayoutInflater inflater = LayoutInflater.from(theme);
      mLayout = (ViewAnimator) inflater.inflate(R.layout.passphrase_dialog, null);
      alert.setView(mLayout);

      mPassphraseText = (TextView) mLayout.findViewById(R.id.passphrase_text);
      mPassphraseEditText = (EditText) mLayout.findViewById(R.id.passphrase_passphrase);

      View vTimeToLiveLayout = mLayout.findViewById(R.id.remember_layout);
      vTimeToLiveLayout.setVisibility(mRequiredInput.mSkipCaching ? View.GONE : View.VISIBLE);

      mTimeToLiveSpinner = (CacheTTLSpinner) mLayout.findViewById(R.id.ttl_spinner);

      alert.setNegativeButton(
          android.R.string.cancel,
          new DialogInterface.OnClickListener() {

            @Override
            public void onClick(DialogInterface dialog, int id) {
              dialog.cancel();
            }
          });

      String userId;
      CanonicalizedSecretKey.SecretKeyType keyType =
          CanonicalizedSecretKey.SecretKeyType.PASSPHRASE;

      String message;
      String hint;
      if (mRequiredInput.mType == RequiredInputType.PASSPHRASE_SYMMETRIC) {
        message = getString(R.string.passphrase_for_symmetric_encryption);
        hint = getString(R.string.label_passphrase);
      } else {
        try {
          ProviderHelper helper = new ProviderHelper(activity);
          mSecretRing =
              helper.getCanonicalizedSecretKeyRing(
                  KeychainContract.KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(subKeyId));
          // yes the inner try/catch block is necessary, otherwise the final variable
          // above can't be statically verified to have been set in all cases because
          // the catch clause doesn't return.
          try {
            String mainUserId = mSecretRing.getPrimaryUserIdWithFallback();
            KeyRing.UserId mainUserIdSplit = KeyRing.splitUserId(mainUserId);
            if (mainUserIdSplit.name != null) {
              userId = mainUserIdSplit.name;
            } else {
              userId = getString(R.string.user_id_no_name);
            }
          } catch (PgpKeyNotFoundException e) {
            userId = null;
          }

          keyType = mSecretRing.getSecretKey(subKeyId).getSecretKeyType();
          switch (keyType) {
            case PASSPHRASE:
              message = getString(R.string.passphrase_for, userId);
              hint = getString(R.string.label_passphrase);
              break;
            case PIN:
              message = getString(R.string.pin_for, userId);
              hint = getString(R.string.label_pin);
              break;
            case DIVERT_TO_CARD:
              message = getString(R.string.yubikey_pin_for, userId);
              hint = getString(R.string.label_pin);
              break;
              // special case: empty passphrase just returns the empty passphrase
            case PASSPHRASE_EMPTY:
              finishCaching(new Passphrase(""));
            default:
              throw new AssertionError("Unhandled SecretKeyType (should not happen)");
          }

        } catch (ProviderHelper.NotFoundException e) {
          alert.setTitle(R.string.title_key_not_found);
          alert.setMessage(getString(R.string.key_not_found, mRequiredInput.getSubKeyId()));
          alert.setPositiveButton(
              android.R.string.ok,
              new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                  dismiss();
                }
              });
          alert.setCancelable(false);
          return alert.create();
        }
      }

      mPassphraseText.setText(message);
      mPassphraseEditText.setHint(hint);

      // Hack to open keyboard.
      // This is the only method that I found to work across all Android versions
      // http://turbomanage.wordpress.com/2012/05/02/show-soft-keyboard-automatically-when-edittext-receives-focus/
      // Notes: * onCreateView can't be used because we want to add buttons to the dialog
      //        * opening in onActivityCreated does not work on Android 4.4
      mPassphraseEditText.setOnFocusChangeListener(
          new View.OnFocusChangeListener() {
            @Override
            public void onFocusChange(View v, boolean hasFocus) {
              mPassphraseEditText.post(
                  new Runnable() {
                    @Override
                    public void run() {
                      if (getActivity() == null || mPassphraseEditText == null) {
                        return;
                      }
                      InputMethodManager imm =
                          (InputMethodManager)
                              getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
                      imm.showSoftInput(mPassphraseEditText, InputMethodManager.SHOW_IMPLICIT);
                    }
                  });
            }
          });
      mPassphraseEditText.requestFocus();

      mPassphraseEditText.setImeActionLabel(
          getString(android.R.string.ok), EditorInfo.IME_ACTION_DONE);
      mPassphraseEditText.setOnEditorActionListener(this);

      if ((keyType == CanonicalizedSecretKey.SecretKeyType.DIVERT_TO_CARD
              && Preferences.getPreferences(activity).useNumKeypadForYubiKeyPin())
          || keyType == CanonicalizedSecretKey.SecretKeyType.PIN) {
        mPassphraseEditText.setInputType(InputType.TYPE_CLASS_NUMBER);
        mPassphraseEditText.setTransformationMethod(PasswordTransformationMethod.getInstance());
      } else {
        mPassphraseEditText.setRawInputType(
            InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
      }

      mPassphraseEditText.setTransformationMethod(PasswordTransformationMethod.getInstance());

      AlertDialog dialog = alert.create();
      dialog.setButton(
          DialogInterface.BUTTON_POSITIVE,
          activity.getString(R.string.btn_unlock),
          (DialogInterface.OnClickListener) null);

      return dialog;
    }
  public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
    // Swap the new cursor in. (The framework will take care of closing the
    // old cursor once we return.)
    switch (loader.getId()) {
      case LOADER_ID_KEYRING:
        if (data.moveToFirst()) {
          // get name, email, and comment from USER_ID
          String[] mainUserId = PgpKeyHelper.splitUserId(data.getString(KEYRING_INDEX_USER_ID));
          if (mainUserId[0] != null) {
            getActivity().setTitle(mainUserId[0]);
            mName.setText(mainUserId[0]);
          } else {
            getActivity().setTitle(R.string.user_id_no_name);
            mName.setText(R.string.user_id_no_name);
          }
          mEmail.setText(mainUserId[1]);
          mComment.setText(mainUserId[2]);
        }

        break;
      case LOADER_ID_USER_IDS:
        mUserIdsAdapter.swapCursor(data);
        break;
      case LOADER_ID_KEYS:
        // the first key here is our master key
        if (data.moveToFirst()) {
          // get key id from MASTER_KEY_ID
          long keyId = data.getLong(KEYS_INDEX_KEY_ID);
          String keyIdStr = PgpKeyHelper.convertKeyIdToHex(keyId);
          mKeyId.setText(keyIdStr);

          // get creation date from CREATION
          if (data.isNull(KEYS_INDEX_CREATION)) {
            mCreation.setText(R.string.none);
          } else {
            Date creationDate = new Date(data.getLong(KEYS_INDEX_CREATION) * 1000);

            mCreation.setText(
                DateFormat.getDateFormat(getActivity().getApplicationContext())
                    .format(creationDate));
          }

          // get expiry date from EXPIRY
          if (data.isNull(KEYS_INDEX_EXPIRY)) {
            mExpiry.setText(R.string.none);
          } else {
            Date expiryDate = new Date(data.getLong(KEYS_INDEX_EXPIRY) * 1000);

            mExpiry.setText(
                DateFormat.getDateFormat(getActivity().getApplicationContext()).format(expiryDate));
          }

          String algorithmStr =
              PgpKeyHelper.getAlgorithmInfo(
                  data.getInt(KEYS_INDEX_ALGORITHM), data.getInt(KEYS_INDEX_KEY_SIZE));
          mAlgorithm.setText(algorithmStr);

          byte[] fingerprintBlob = data.getBlob(KEYS_INDEX_FINGERPRINT);
          if (fingerprintBlob == null) {
            // FALLBACK for old database entries
            fingerprintBlob = ProviderHelper.getFingerprint(getActivity(), mDataUri);
          }
          String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob, true);

          mFingerprint.setText(OtherHelper.colorizeFingerprint(fingerprint));
        }

        mKeysAdapter.swapCursor(data);
        break;

      default:
        break;
    }
  }
  private void share(boolean fingerprintOnly, boolean toClipboard) {
    Activity activity = getActivity();
    if (activity == null || mFingerprint == null) {
      return;
    }
    ProviderHelper providerHelper = new ProviderHelper(activity);

    try {
      String content;
      if (fingerprintOnly) {
        String fingerprint = KeyFormattingUtils.convertFingerprintToHex(mFingerprint);
        if (!toClipboard) {
          content = Constants.FINGERPRINT_SCHEME + ":" + fingerprint;
        } else {
          content = fingerprint;
        }
      } else {
        content =
            providerHelper.getKeyRingAsArmoredString(
                KeychainContract.KeyRingData.buildPublicKeyRingUri(mDataUri));
      }

      if (toClipboard) {
        ClipboardManager clipMan =
            (ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE);
        if (clipMan == null) {
          Notify.create(activity, R.string.error_clipboard_copy, Style.ERROR);
          return;
        }

        ClipData clip = ClipData.newPlainText(Constants.CLIPBOARD_LABEL, content);
        clipMan.setPrimaryClip(clip);

        Notify.create(
                activity,
                fingerprintOnly
                    ? R.string.fingerprint_copied_to_clipboard
                    : R.string.key_copied_to_clipboard,
                Notify.Style.OK)
            .show();
        return;
      }

      // Android will fail with android.os.TransactionTooLargeException if key is too big
      // see http://www.lonestarprod.com/?p=34
      if (content.length() >= 86389) {
        Notify.create(activity, R.string.key_too_big_for_sharing, Notify.Style.ERROR).show();
        return;
      }

      // let user choose application
      Intent sendIntent = new Intent(Intent.ACTION_SEND);
      sendIntent.putExtra(Intent.EXTRA_TEXT, content);
      sendIntent.setType("text/plain");

      // Bluetooth Share will convert text/plain sent via EXTRA_TEXT to HTML
      // Add replacement extra to send a text/plain file instead.
      try {
        TemporaryStorageProvider shareFileProv = new TemporaryStorageProvider();
        Uri contentUri =
            TemporaryStorageProvider.createFile(
                activity,
                KeyFormattingUtils.convertFingerprintToHex(mFingerprint)
                    + Constants.FILE_EXTENSION_ASC);

        BufferedWriter contentWriter =
            new BufferedWriter(
                new OutputStreamWriter(
                    new ParcelFileDescriptor.AutoCloseOutputStream(
                        shareFileProv.openFile(contentUri, "w"))));
        contentWriter.write(content);
        contentWriter.close();

        sendIntent.putExtra(Intent.EXTRA_STREAM, contentUri);
      } catch (FileNotFoundException e) {
        Log.e(Constants.TAG, "error creating temporary Bluetooth key share file!", e);
        // no need for a snackbar because one sharing option doesn't work
        // Notify.create(getActivity(), R.string.error_temp_file, Notify.Style.ERROR).show();
      }

      String title =
          getString(
              fingerprintOnly ? R.string.title_share_fingerprint_with : R.string.title_share_key);
      Intent shareChooser = Intent.createChooser(sendIntent, title);

      startActivity(shareChooser);

    } catch (PgpGeneralException | IOException e) {
      Log.e(Constants.TAG, "error processing key!", e);
      Notify.create(activity, R.string.error_key_processing, Notify.Style.ERROR).show();
    } catch (ProviderHelper.NotFoundException e) {
      Log.e(Constants.TAG, "key not found!", e);
      Notify.create(activity, R.string.error_key_not_found, Notify.Style.ERROR).show();
    }
  }