+ // Initialize the cipher.
+ cipher.init(Cipher.ENCRYPT_MODE, secretKey, gcmParameterSpec);
+
+ // Get the file name string.
+ String passwordEncryptionFileNameString = fileNameEditText.getText().toString();
+
+ // Get the export file output stream.
+ OutputStream exportFileOutputStream = getContentResolver().openOutputStream(Uri.parse(passwordEncryptionFileNameString));
+
+ // Add the salt and the initialization vector to the export file output stream.
+ exportFileOutputStream.write(saltByteArray);
+ exportFileOutputStream.write(initializationVector);
+
+ // Create a cipher output stream.
+ CipherOutputStream cipherOutputStream = new CipherOutputStream(exportFileOutputStream, cipher);
+
+ // Initialize variables to store data as it is moved from the unencrypted export file input stream to the cipher output stream. Move 128 bits (16 bytes) at a time.
+ int numberOfBytesRead;
+ byte[] encryptedBytes = new byte[16];
+
+ // Read up to 128 bits (16 bytes) of data from the unencrypted export file stream. `-1` will be returned when the end of the file is reached.
+ while ((numberOfBytesRead = unencryptedExportFileInputStream.read(encryptedBytes)) != -1) {
+ // Write the data to the cipher output stream.
+ cipherOutputStream.write(encryptedBytes, 0, numberOfBytesRead);
+ }
+
+ // Close the streams.
+ cipherOutputStream.flush();
+ cipherOutputStream.close();
+ exportFileOutputStream.close();
+ unencryptedExportFileInputStream.close();
+
+ // Wipe the encryption data from memory.
+ //noinspection UnusedAssignment
+ encryptionPasswordString = "";
+ Arrays.fill(saltByteArray, (byte) 0);
+ Arrays.fill(encryptionPasswordByteArray, (byte) 0);
+ Arrays.fill(encryptionPasswordWithSaltByteArray, (byte) 0);
+ Arrays.fill(hashedEncryptionPasswordWithSaltByteArray, (byte) 0);
+ Arrays.fill(truncatedHashedEncryptionPasswordWithSaltByteArray, (byte) 0);
+ Arrays.fill(initializationVector, (byte) 0);
+ Arrays.fill(encryptedBytes, (byte) 0);
+
+ // Delete the temporary unencrypted export file.
+ //noinspection ResultOfMethodCallIgnored
+ temporaryUnencryptedExportFile.delete();
+
+ // Display an export disposition snackbar.
+ if (passwordEncryptionExportStatus.equals(ImportExportDatabaseHelper.EXPORT_SUCCESSFUL)) {
+ Snackbar.make(fileNameEditText, getString(R.string.export_successful), Snackbar.LENGTH_SHORT).show();
+ } else {
+ Snackbar.make(fileNameEditText, getString(R.string.export_failed) + " " + passwordEncryptionExportStatus, Snackbar.LENGTH_INDEFINITE).show();
+ }
+ } catch (Exception exception) {
+ // Display a snackbar with the exception.
+ Snackbar.make(fileNameEditText, getString(R.string.export_failed) + " " + exception, Snackbar.LENGTH_INDEFINITE).show();
+ }
+ break;
+
+ case OPENPGP_ENCRYPTION:
+ try {
+ // Get a handle for the file provider directory.
+ fileProviderDirectory = new File(getApplicationContext().getCacheDir() + "/" + getString(R.string.file_provider_directory));
+
+ // Create the file provider directory. Any errors will be handled by the catch statement below.
+ //noinspection ResultOfMethodCallIgnored
+ fileProviderDirectory.mkdir();
+
+ // Set the temporary pre-encrypted export file.
+ temporaryPreEncryptedExportFile = new File(fileProviderDirectory + "/" + getString(R.string.settings) + " " + BuildConfig.VERSION_NAME + ".pbs");
+
+ // Delete the temporary pre-encrypted export file if it already exists.
+ if (temporaryPreEncryptedExportFile.exists()) {
+ //noinspection ResultOfMethodCallIgnored
+ temporaryPreEncryptedExportFile.delete();
+ }
+
+ // Create a temporary pre-encrypted export output stream.
+ FileOutputStream temporaryPreEncryptedExportOutputStream = new FileOutputStream(temporaryPreEncryptedExportFile);
+
+ // Populate the temporary pre-encrypted export file.
+ String openpgpEncryptionExportStatus = importExportDatabaseHelper.exportUnencrypted(temporaryPreEncryptedExportOutputStream, this);
+
+ // Flush the temporary pre-encryption export output stream.
+ temporaryPreEncryptedExportOutputStream.flush();
+
+ // Close the temporary pre-encryption export output stream.
+ temporaryPreEncryptedExportOutputStream.close();
+
+ // Display an export error snackbar if the temporary pre-encrypted export failed.
+ if (!openpgpEncryptionExportStatus.equals(ImportExportDatabaseHelper.EXPORT_SUCCESSFUL)) {
+ Snackbar.make(fileNameEditText, getString(R.string.export_failed) + " " + openpgpEncryptionExportStatus, Snackbar.LENGTH_INDEFINITE).show();
+ }
+
+ // Create an encryption intent for OpenKeychain.
+ Intent openKeychainEncryptIntent = new Intent("org.sufficientlysecure.keychain.action.ENCRYPT_DATA");
+
+ // Include the temporary unencrypted export file URI.
+ openKeychainEncryptIntent.setData(FileProvider.getUriForFile(this, getString(R.string.file_provider), temporaryPreEncryptedExportFile));
+
+ // Allow OpenKeychain to read the file URI.
+ openKeychainEncryptIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+ // Send the intent to the OpenKeychain package.
+ openKeychainEncryptIntent.setPackage("org.sufficientlysecure.keychain");
+
+ // Make it so.
+ startActivityForResult(openKeychainEncryptIntent, OPENPGP_EXPORT_RESULT_CODE);
+ } catch (Exception exception) {
+ // Display a snackbar with the exception.
+ Snackbar.make(fileNameEditText, getString(R.string.export_failed) + " " + exception, Snackbar.LENGTH_INDEFINITE).show();
+ }
+ break;
+ }