/** 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();
      }
    }
  }
Пример #2
0
  /**
   * The IntentService calls this method from the default worker thread with the intent that started
   * the service. When this method returns, IntentService stops the service, as appropriate.
   */
  @Override
  protected void onHandleIntent(Intent intent) {
    Bundle extras = intent.getExtras();
    if (extras == null) {
      Log.e(Constants.TAG, "Extras bundle is null!");
      return;
    }

    if (!(extras.containsKey(EXTRA_MESSENGER)
        || extras.containsKey(EXTRA_DATA)
        || (intent.getAction() == null))) {
      Log.e(Constants.TAG, "Extra bundle must contain a messenger, a data bundle, and an action!");
      return;
    }

    Uri dataUri = intent.getData();

    mMessenger = (Messenger) extras.get(EXTRA_MESSENGER);
    Bundle data = extras.getBundle(EXTRA_DATA);

    OtherHelper.logDebugBundle(data, "EXTRA_DATA");

    String action = intent.getAction();

    // executeServiceMethod action from extra bundle
    if (ACTION_ENCRYPT_SIGN.equals(action)
        || "org.thialfihar.android.apg.intent.ENCRYPT_AND_RETURN".equals(action)) {
      try {
        /* Input */
        int target = data.getInt(TARGET);

        long signatureKeyId = data.getLong(ENCRYPT_SIGNATURE_KEY_ID);
        String symmetricPassphrase = data.getString(ENCRYPT_SYMMETRIC_PASSPHRASE);

        boolean useAsciiArmor = data.getBoolean(ENCRYPT_USE_ASCII_ARMOR);
        long encryptionKeyIds[] = data.getLongArray(ENCRYPT_ENCRYPTION_KEYS_IDS);
        int compressionId = data.getInt(ENCRYPT_COMPRESSION_ID);
        boolean generateSignature = data.getBoolean(ENCRYPT_GENERATE_SIGNATURE);
        InputStream inStream;
        long inLength;
        InputData inputData;
        OutputStream outStream;
        //                String streamFilename = null;
        switch (target) {
          case TARGET_BYTES: /* encrypting bytes directly */
            byte[] bytes = data.getByteArray(ENCRYPT_MESSAGE_BYTES);

            inStream = new ByteArrayInputStream(bytes);
            inLength = bytes.length;

            inputData = new InputData(inStream, inLength);
            outStream = new ByteArrayOutputStream();

            break;
          case TARGET_URI: /* encrypting file */
            String inputFile = data.getString(ENCRYPT_INPUT_FILE);
            String outputFile = data.getString(ENCRYPT_OUTPUT_FILE);

            // check if storage is ready
            if (!FileHelper.isStorageMounted(inputFile)
                || !FileHelper.isStorageMounted(outputFile)) {
              throw new PgpGeneralException(getString(R.string.error_external_storage_not_ready));
            }

            inStream = new FileInputStream(inputFile);
            File file = new File(inputFile);
            inLength = file.length();
            inputData = new InputData(inStream, inLength);

            outStream = new FileOutputStream(outputFile);

            break;

            // TODO: not used currently
            //                    case TARGET_STREAM: /* Encrypting stream from content uri */
            //                        Uri providerUri = (Uri)
            // data.getParcelable(ENCRYPT_PROVIDER_URI);
            //
            //                        // InputStream
            //                        InputStream in =
            // getContentResolver().openInputStream(providerUri);
            //                        inLength = PgpHelper.getLengthOfStream(in);
            //                        inputData = new InputData(in, inLength);
            //
            //                        // OutputStream
            //                        try {
            //                            while (true) {
            //                                streamFilename = PgpHelper.generateRandomFilename(32);
            //                                if (streamFilename == null) {
            //                                    throw new PgpGeneralException("couldn't generate
            // random file name");
            //                                }
            //                                openFileInput(streamFilename).close();
            //                            }
            //                        } catch (FileNotFoundException e) {
            //                            // found a name that isn't used yet
            //                        }
            //                        outStream = openFileOutput(streamFilename,
            // Context.MODE_PRIVATE);
            //
            //                        break;

          default:
            throw new PgpGeneralException("No target choosen!");
        }

        /* Operation */
        PgpSignEncrypt.Builder builder =
            new PgpSignEncrypt.Builder(this, inputData, outStream, new ProviderHelper(this));
        builder.setProgressable(this);

        if (generateSignature) {
          Log.d(Constants.TAG, "generating signature...");
          builder
              .setEnableAsciiArmorOutput(useAsciiArmor)
              .setSignatureForceV3(Preferences.getPreferences(this).getForceV3Signatures())
              .setSignatureKeyId(signatureKeyId)
              .setSignatureHashAlgorithm(Preferences.getPreferences(this).getDefaultHashAlgorithm())
              .setSignaturePassphrase(
                  PassphraseCacheService.getCachedPassphrase(this, signatureKeyId));

          builder.build().generateSignature();
        } else {
          Log.d(Constants.TAG, "encrypt...");
          builder
              .setEnableAsciiArmorOutput(useAsciiArmor)
              .setCompressionId(compressionId)
              .setSymmetricEncryptionAlgorithm(
                  Preferences.getPreferences(this).getDefaultEncryptionAlgorithm())
              .setSignatureForceV3(Preferences.getPreferences(this).getForceV3Signatures())
              .setEncryptionKeyIds(encryptionKeyIds)
              .setSymmetricPassphrase(symmetricPassphrase)
              .setSignatureKeyId(signatureKeyId)
              .setSignatureHashAlgorithm(Preferences.getPreferences(this).getDefaultHashAlgorithm())
              .setSignaturePassphrase(
                  PassphraseCacheService.getCachedPassphrase(this, signatureKeyId));

          builder.build().execute();
        }

        outStream.close();

        /* Output */

        Bundle resultData = new Bundle();

        switch (target) {
          case TARGET_BYTES:
            byte output[] = ((ByteArrayOutputStream) outStream).toByteArray();

            resultData.putByteArray(RESULT_BYTES, output);

            break;
          case TARGET_URI:
            // nothing, file was written, just send okay

            break;
            //                    case TARGET_STREAM:
            //                        String uri =
            // DataStream.buildDataStreamUri(streamFilename).toString();
            //                        resultData.putString(RESULT_URI, uri);
            //
            //                        break;
        }

        OtherHelper.logDebugBundle(resultData, "resultData");

        sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_OKAY, resultData);
      } catch (Exception e) {
        sendErrorToHandler(e);
      }
    } else if (ACTION_DECRYPT_VERIFY.equals(action)) {
      try {
        /* Input */
        int target = data.getInt(TARGET);

        byte[] bytes = data.getByteArray(DECRYPT_CIPHERTEXT_BYTES);
        String passphrase = data.getString(DECRYPT_PASSPHRASE);

        InputStream inStream;
        long inLength;
        InputData inputData;
        OutputStream outStream;
        String streamFilename = null;
        switch (target) {
          case TARGET_BYTES: /* decrypting bytes directly */
            inStream = new ByteArrayInputStream(bytes);
            inLength = bytes.length;

            inputData = new InputData(inStream, inLength);
            outStream = new ByteArrayOutputStream();

            break;

          case TARGET_URI: /* decrypting file */
            String inputFile = data.getString(ENCRYPT_INPUT_FILE);
            String outputFile = data.getString(ENCRYPT_OUTPUT_FILE);

            // check if storage is ready
            if (!FileHelper.isStorageMounted(inputFile)
                || !FileHelper.isStorageMounted(outputFile)) {
              throw new PgpGeneralException(getString(R.string.error_external_storage_not_ready));
            }

            // InputStream
            inLength = -1;
            inStream = new FileInputStream(inputFile);
            File file = new File(inputFile);
            inLength = file.length();
            inputData = new InputData(inStream, inLength);

            // OutputStream
            outStream = new FileOutputStream(outputFile);

            break;

            // TODO: not used, maybe contains code useful for new decrypt method for files?
            //                    case TARGET_STREAM: /* decrypting stream from content uri */
            //                        Uri providerUri = (Uri)
            // data.getParcelable(ENCRYPT_PROVIDER_URI);
            //
            //                        // InputStream
            //                        InputStream in =
            // getContentResolver().openInputStream(providerUri);
            //                        inLength = PgpHelper.getLengthOfStream(in);
            //                        inputData = new InputData(in, inLength);
            //
            //                        // OutputStream
            //                        try {
            //                            while (true) {
            //                                streamFilename = PgpHelper.generateRandomFilename(32);
            //                                if (streamFilename == null) {
            //                                    throw new PgpGeneralException("couldn't generate
            // random file name");
            //                                }
            //                                openFileInput(streamFilename).close();
            //                            }
            //                        } catch (FileNotFoundException e) {
            //                            // found a name that isn't used yet
            //                        }
            //                        outStream = openFileOutput(streamFilename,
            // Context.MODE_PRIVATE);
            //
            //                        break;

          default:
            throw new PgpGeneralException("No target choosen!");
        }

        /* Operation */

        Bundle resultData = new Bundle();

        // verifyText and decrypt returning additional resultData values for the
        // verification of signatures
        PgpDecryptVerify.Builder builder =
            new PgpDecryptVerify.Builder(this, inputData, outStream, new ProviderHelper(this));
        builder.setProgressable(this);

        builder.setAllowSymmetricDecryption(true).setPassphrase(passphrase);

        PgpDecryptVerifyResult decryptVerifyResult = builder.build().execute();

        outStream.close();

        resultData.putParcelable(RESULT_DECRYPT_VERIFY_RESULT, decryptVerifyResult);

        /* Output */

        switch (target) {
          case TARGET_BYTES:
            byte output[] = ((ByteArrayOutputStream) outStream).toByteArray();
            resultData.putByteArray(RESULT_DECRYPTED_BYTES, output);
            break;
          case TARGET_URI:
            // nothing, file was written, just send okay and verification bundle

            break;
            //                    case TARGET_STREAM:
            //                        String uri =
            // DataStream.buildDataStreamUri(streamFilename).toString();
            //                        resultData.putString(RESULT_URI, uri);
            //
            //                        break;
        }

        OtherHelper.logDebugBundle(resultData, "resultData");

        sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_OKAY, resultData);
      } catch (Exception e) {
        sendErrorToHandler(e);
      }
    } else if (ACTION_SAVE_KEYRING.equals(action)) {
      try {
        /* Input */
        SaveKeyringParcel saveParams = data.getParcelable(SAVE_KEYRING_PARCEL);
        String oldPassphrase = saveParams.oldPassphrase;
        String newPassphrase = saveParams.newPassphrase;
        boolean canSign = true;

        if (data.containsKey(SAVE_KEYRING_CAN_SIGN)) {
          canSign = data.getBoolean(SAVE_KEYRING_CAN_SIGN);
        }

        if (newPassphrase == null) {
          newPassphrase = oldPassphrase;
        }

        long masterKeyId = saveParams.keys.get(0).getKeyId();

        /* Operation */
        if (!canSign) {
          PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 50, 100));
          PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(this, masterKeyId);
          keyRing = keyOperations.changeSecretKeyPassphrase(keyRing, oldPassphrase, newPassphrase);
          setProgress(R.string.progress_saving_key_ring, 50, 100);
          ProviderHelper.saveKeyRing(this, keyRing);
          setProgress(R.string.progress_done, 100, 100);
        } else {
          PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 90, 100));
          PGPSecretKeyRing privkey =
              ProviderHelper.getPGPSecretKeyRingByMasterKeyId(this, masterKeyId);
          PGPPublicKeyRing pubkey =
              ProviderHelper.getPGPPublicKeyRingByMasterKeyId(this, masterKeyId);
          PgpKeyOperation.Pair<PGPSecretKeyRing, PGPPublicKeyRing> pair =
              keyOperations.buildSecretKey(privkey, pubkey, saveParams);
          setProgress(R.string.progress_saving_key_ring, 90, 100);
          ProviderHelper.saveKeyRing(this, pair.first);
          ProviderHelper.saveKeyRing(this, pair.second);
          setProgress(R.string.progress_done, 100, 100);
        }
        PassphraseCacheService.addCachedPassphrase(this, masterKeyId, newPassphrase);

        /* Output */
        sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_OKAY);
      } catch (Exception e) {
        sendErrorToHandler(e);
      }
    } else if (ACTION_GENERATE_KEY.equals(action)) {
      try {
        /* Input */
        int algorithm = data.getInt(GENERATE_KEY_ALGORITHM);
        String passphrase = data.getString(GENERATE_KEY_SYMMETRIC_PASSPHRASE);
        int keysize = data.getInt(GENERATE_KEY_KEY_SIZE);
        boolean masterKey = data.getBoolean(GENERATE_KEY_MASTER_KEY);

        /* Operation */
        PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 100, 100));
        Key newKey = keyOperations.createKey(algorithm, keysize, passphrase, masterKey);

        /* Output */
        Bundle resultData = new Bundle();
        resultData.putSerializable(RESULT_NEW_KEY, newKey);

        OtherHelper.logDebugBundle(resultData, "resultData");

        sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_OKAY, resultData);
      } catch (Exception e) {
        sendErrorToHandler(e);
      }
    } else if (ACTION_GENERATE_DEFAULT_RSA_KEYS.equals(action)) {
      // generate one RSA 4096 key for signing and one subkey for encrypting!
      try {
        /* Input */
        String passphrase = data.getString(GENERATE_KEY_SYMMETRIC_PASSPHRASE);
        ArrayList<Key> newKeys = new ArrayList<Key>();
        ArrayList<Integer> keyUsageList = new ArrayList<Integer>();

        /* Operation */
        int keysTotal = 3;
        int keysCreated = 0;
        setProgress(
            getApplicationContext()
                .getResources()
                .getQuantityString(R.plurals.progress_generating, keysTotal),
            keysCreated,
            keysTotal);
        PgpKeyOperation keyOperations = new PgpKeyOperation(new ProgressScaler(this, 0, 100, 100));

        Key masterKey = keyOperations.createKey(Id.choice.algorithm.rsa, 4096, passphrase, true);
        newKeys.add(masterKey);
        keyUsageList.add(KeyFlags.CERTIFY_OTHER);
        keysCreated++;
        setProgress(keysCreated, keysTotal);

        Key subKey = keyOperations.createKey(Id.choice.algorithm.rsa, 4096, passphrase, false);
        newKeys.add(subKey);
        keyUsageList.add(KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE);
        keysCreated++;
        setProgress(keysCreated, keysTotal);

        subKey = keyOperations.createKey(Id.choice.algorithm.rsa, 4096, passphrase, false);
        newKeys.add(subKey);
        keyUsageList.add(KeyFlags.SIGN_DATA);
        keysCreated++;
        setProgress(keysCreated, keysTotal);

        // TODO: default to one master for cert, one sub for encrypt and one sub
        //       for sign

        /* Output */

        Bundle resultData = new Bundle();
        resultData.putSerializable(RESULT_NEW_KEY, newKeys);
        resultData.putIntegerArrayList(RESULT_KEY_USAGES, keyUsageList);

        OtherHelper.logDebugBundle(resultData, "resultData");

        sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_OKAY, resultData);
      } catch (Exception e) {
        sendErrorToHandler(e);
      }
    } else if (ACTION_DELETE_FILE_SECURELY.equals(action)) {
      try {
        /* Input */
        String deleteFile = data.getString(DELETE_FILE);

        /* Operation */
        try {
          PgpHelper.deleteFileSecurely(this, this, new File(deleteFile));
        } catch (FileNotFoundException e) {
          throw new PgpGeneralException(getString(R.string.error_file_not_found, deleteFile));
        } catch (IOException e) {
          throw new PgpGeneralException(getString(R.string.error_file_delete_failed, deleteFile));
        }

        /* Output */
        sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_OKAY);
      } catch (Exception e) {
        sendErrorToHandler(e);
      }
    } else if (ACTION_IMPORT_KEYRING.equals(action)) {
      try {
        List<ImportKeysListEntry> entries = data.getParcelableArrayList(IMPORT_KEY_LIST);

        Bundle resultData = new Bundle();

        PgpImportExport pgpImportExport = new PgpImportExport(this, this);
        resultData = pgpImportExport.importKeyRings(entries);

        sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_OKAY, resultData);
      } catch (Exception e) {
        sendErrorToHandler(e);
      }
    } else if (ACTION_EXPORT_KEYRING.equals(action)) {
      try {

        /* Input */
        int keyType = Id.type.public_key;
        if (data.containsKey(EXPORT_KEY_TYPE)) {
          keyType = data.getInt(EXPORT_KEY_TYPE);
        }
        long[] masterKeyIds = data.getLongArray(EXPORT_KEY_RING_MASTER_KEY_ID);
        String outputFile = data.getString(EXPORT_FILENAME);

        // If not exporting all keys get the masterKeyIds of the keys to export from the intent
        boolean exportAll = data.getBoolean(EXPORT_ALL);

        /* Operation */

        // check if storage is ready
        if (!FileHelper.isStorageMounted(outputFile)) {
          throw new PgpGeneralException(getString(R.string.error_external_storage_not_ready));
        }

        ArrayList<Long> publicMasterKeyIds = new ArrayList<Long>();
        ArrayList<Long> secretMasterKeyIds = new ArrayList<Long>();
        ArrayList<Long> allPublicMasterKeyIds = ProviderHelper.getPublicKeyRingsMasterKeyIds(this);
        ArrayList<Long> allSecretMasterKeyIds = ProviderHelper.getSecretKeyRingsMasterKeyIds(this);

        if (exportAll) {
          // get all public key ring MasterKey ids
          if (keyType == Id.type.public_key || keyType == Id.type.public_secret_key) {
            publicMasterKeyIds = allPublicMasterKeyIds;
          }
          // get all secret key ring MasterKey ids
          if (keyType == Id.type.secret_key || keyType == Id.type.public_secret_key) {
            secretMasterKeyIds = allSecretMasterKeyIds;
          }
        } else {

          for (long masterKeyId : masterKeyIds) {
            if ((keyType == Id.type.public_key || keyType == Id.type.public_secret_key)
                && allPublicMasterKeyIds.contains(masterKeyId)) {
              publicMasterKeyIds.add(masterKeyId);
            }
            if ((keyType == Id.type.secret_key || keyType == Id.type.public_secret_key)
                && allSecretMasterKeyIds.contains(masterKeyId)) {
              secretMasterKeyIds.add(masterKeyId);
            }
          }
        }

        PgpImportExport pgpImportExport = new PgpImportExport(this, this, this);

        Bundle resultData =
            pgpImportExport.exportKeyRings(
                publicMasterKeyIds, secretMasterKeyIds, new FileOutputStream(outputFile));

        if (mIsCanceled) {
          new File(outputFile).delete();
        }

        sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_OKAY, resultData);
      } catch (Exception e) {
        sendErrorToHandler(e);
      }
    } else if (ACTION_UPLOAD_KEYRING.equals(action)) {
      try {

        /* Input */
        String keyServer = data.getString(UPLOAD_KEY_SERVER);
        // and dataUri!

        /* Operation */
        HkpKeyServer server = new HkpKeyServer(keyServer);

        KeyRing keyRing = ProviderHelper.getKeyRing(this, dataUri);
        if (keyRing != null) {
          PgpImportExport pgpImportExport = new PgpImportExport(this, null);

          boolean uploaded =
              pgpImportExport.uploadKeyRingToServer(server, new PublicKeyRing(keyRing));
          if (!uploaded) {
            throw new PgpGeneralException("Unable to export key to selected server");
          }
        }

        sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_OKAY);
      } catch (Exception e) {
        sendErrorToHandler(e);
      }
    } else if (ACTION_DOWNLOAD_AND_IMPORT_KEYS.equals(action)) {
      try {
        ArrayList<ImportKeysListEntry> entries = data.getParcelableArrayList(DOWNLOAD_KEY_LIST);
        String keyServer = data.getString(DOWNLOAD_KEY_SERVER);

        // TODO: add extra which requires fingerprint suport and force verification!
        // only supported by newer sks keyserver versions

        // this downloads the keys and places them into the ImportKeysListEntry entries
        HkpKeyServer server = new HkpKeyServer(keyServer);

        for (ImportKeysListEntry entry : entries) {
          // if available use complete fingerprint for get request
          byte[] downloadedKeyBytes;
          if (entry.getFingerPrintHex() != null) {
            downloadedKeyBytes = server.get("0x" + entry.getFingerPrintHex()).getBytes();
          } else {
            downloadedKeyBytes = server.get(entry.getKeyIdHex()).getBytes();
          }

          // create PGPKeyRing object based on downloaded armored key
          PGPKeyRing downloadedKey = null;
          BufferedInputStream bufferedInput =
              new BufferedInputStream(new ByteArrayInputStream(downloadedKeyBytes));
          if (bufferedInput.available() > 0) {
            InputStream in = PGPUtil.getDecoderStream(bufferedInput);
            PGPObjectFactory objectFactory = new PGPObjectFactory(in);

            // get first object in block
            Object obj;
            if ((obj = objectFactory.nextObject()) != null) {
              Log.d(Constants.TAG, "Found class: " + obj.getClass());

              if (obj instanceof PGPKeyRing) {
                downloadedKey = (PGPKeyRing) obj;
              } else {
                throw new PgpGeneralException("Object not recognized as PGPKeyRing!");
              }
            }
          }

          // verify downloaded key by comparing fingerprints
          if (entry.getFingerPrintHex() != null) {
            String downloadedKeyFp =
                PgpKeyHelper.convertFingerprintToHex(downloadedKey.getPublicKey().getFingerprint());
            if (downloadedKeyFp.equals(entry.getFingerPrintHex())) {
              Log.d(
                  Constants.TAG,
                  "fingerprint of downloaded key is the same as " + "the requested fingerprint!");
            } else {
              throw new PgpGeneralException(
                  "fingerprint of downloaded key is "
                      + "NOT the same as the requested fingerprint!");
            }
          }

          // save key bytes in entry object for doing the
          // actual import afterwards
          entry.setBytes(downloadedKey.getEncoded());
        }

        Intent importIntent = new Intent(this, ApgIntentService.class);

        importIntent.setAction(ACTION_IMPORT_KEYRING);
        Bundle importData = new Bundle();
        importData.putParcelableArrayList(IMPORT_KEY_LIST, entries);
        importIntent.putExtra(EXTRA_DATA, importData);
        importIntent.putExtra(EXTRA_MESSENGER, mMessenger);

        // now import it with this service
        onHandleIntent(importIntent);

        // result is handled in ACTION_IMPORT_KEYRING
      } catch (Exception e) {
        sendErrorToHandler(e);
      }
    } else if (ACTION_CERTIFY_KEYRING.equals(action)) {
      try {

        /* Input */
        long masterKeyId = data.getLong(CERTIFY_KEY_MASTER_KEY_ID);
        long pubKeyId = data.getLong(CERTIFY_KEY_PUB_KEY_ID);
        ArrayList<String> userIds = data.getStringArrayList(CERTIFY_KEY_UIDS);

        /* Operation */
        String signaturePassphrase = PassphraseCacheService.getCachedPassphrase(this, masterKeyId);
        if (signaturePassphrase == null) {
          throw new PgpGeneralException("Unable to obtain passphrase");
        }

        PgpKeyOperation keyOperation = new PgpKeyOperation(new ProgressScaler(this, 0, 100, 100));
        PGPPublicKeyRing publicRing = ProviderHelper.getPGPPublicKeyRingByKeyId(this, pubKeyId);
        PGPPublicKey publicKey = publicRing.getPublicKey(pubKeyId);
        PGPSecretKey certificationKey = PgpKeyHelper.getCertificationKey(this, masterKeyId);
        publicKey =
            keyOperation.certifyKey(certificationKey, publicKey, userIds, signaturePassphrase);
        publicRing = PGPPublicKeyRing.insertPublicKey(publicRing, publicKey);

        // store the signed key in our local cache
        PgpImportExport pgpImportExport = new PgpImportExport(this, null);
        int retval = pgpImportExport.storeKeyRingInCache(publicRing);
        if (retval != Id.return_value.ok && retval != Id.return_value.updated) {
          throw new PgpGeneralException("Failed to store signed key in local cache");
        }

        sendMessageToHandler(ApgIntentServiceHandler.MESSAGE_OKAY);
      } catch (Exception e) {
        sendErrorToHandler(e);
      }
    }
  }