diff --git a/app/src/main/java/com/stevesoltys/backup/security/CipherUtil.java b/app/src/main/java/com/stevesoltys/backup/security/CipherUtil.java new file mode 100644 index 00000000..66c8089d --- /dev/null +++ b/app/src/main/java/com/stevesoltys/backup/security/CipherUtil.java @@ -0,0 +1,57 @@ +package com.stevesoltys.backup.security; + +import javax.crypto.*; +import javax.crypto.spec.IvParameterSpec; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; + +/** + * A utility class for encrypting and decrypting data using a {@link Cipher}. + * + * @author Steve Soltys + */ +public class CipherUtil { + + /** + * The cipher algorithm. + */ + public static final String CIPHER_ALGORITHM = "AES/CFB/PKCS5Padding"; + + /** + * Encrypts the given payload using a key generated from the provided password and salt. + * + * @param payload The payload. + * @param password The password. + * @param salt The salt. + */ + public static byte[] encrypt(byte[] payload, String password, byte[] salt) throws NoSuchPaddingException, + NoSuchAlgorithmException, InvalidKeySpecException, BadPaddingException, IllegalBlockSizeException, + InvalidAlgorithmParameterException, InvalidKeyException { + + SecretKey secretKey = KeyGenerator.generate(password, salt); + Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); + cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(salt)); + + return cipher.doFinal(payload); + } + + /** + * Decrypts the given payload using a key generated from the provided password and salt. + * + * @param payload The payload. + * @param password The password. + * @param salt The salt. + */ + public static byte[] decrypt(byte[] payload, String password, byte[] salt) throws NoSuchPaddingException, + NoSuchAlgorithmException, InvalidKeySpecException, BadPaddingException, IllegalBlockSizeException, + InvalidAlgorithmParameterException, InvalidKeyException { + + SecretKey secretKey = KeyGenerator.generate(password, salt); + Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); + cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(salt)); + + return cipher.doFinal(payload); + } +} diff --git a/app/src/main/java/com/stevesoltys/backup/transport/component/provider/ContentProviderBackupComponent.java b/app/src/main/java/com/stevesoltys/backup/transport/component/provider/ContentProviderBackupComponent.java index 56ecae6d..6c037663 100644 --- a/app/src/main/java/com/stevesoltys/backup/transport/component/provider/ContentProviderBackupComponent.java +++ b/app/src/main/java/com/stevesoltys/backup/transport/component/provider/ContentProviderBackupComponent.java @@ -6,19 +6,15 @@ import android.content.pm.PackageInfo; import android.os.ParcelFileDescriptor; import android.util.Base64; import android.util.Log; -import com.stevesoltys.backup.security.KeyGenerator; +import com.stevesoltys.backup.security.CipherUtil; import com.stevesoltys.backup.transport.component.BackupComponent; import libcore.io.IoUtils; import org.apache.commons.io.IOUtils; -import javax.crypto.Cipher; -import javax.crypto.SecretKey; -import javax.crypto.spec.IvParameterSpec; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; @@ -262,14 +258,12 @@ public class ContentProviderBackupComponent implements BackupComponent { try { if (configuration.getPassword() != null && !configuration.getPassword().isEmpty()) { - SecretKey secretKey = KeyGenerator.generate(configuration.getPassword(), backupState.getSalt()); - - Cipher cipher = Cipher.getInstance(ContentProviderBackupConstants.CIPHER_ALGORITHM); - cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(backupState.getSalt())); byte[] payload = Arrays.copyOfRange(buffer, 0, dataSize); - byte[] encryptedBuffer = cipher.doFinal(payload); - outputStream.write(encryptedBuffer); + String password = configuration.getPassword(); + byte[] salt = backupState.getSalt(); + + outputStream.write(CipherUtil.encrypt(payload, password, salt)); } else { outputStream.write(buffer, 0, dataSize); diff --git a/app/src/main/java/com/stevesoltys/backup/transport/component/provider/ContentProviderBackupConstants.java b/app/src/main/java/com/stevesoltys/backup/transport/component/provider/ContentProviderBackupConstants.java index 9ec4235d..b87e30a8 100644 --- a/app/src/main/java/com/stevesoltys/backup/transport/component/provider/ContentProviderBackupConstants.java +++ b/app/src/main/java/com/stevesoltys/backup/transport/component/provider/ContentProviderBackupConstants.java @@ -3,9 +3,7 @@ package com.stevesoltys.backup.transport.component.provider; /** * @author Steve Soltys */ -public class ContentProviderBackupConstants { - - static final String CIPHER_ALGORITHM = "AES/CFB/PKCS5Padding"; +class ContentProviderBackupConstants { static final String SALT_FILE_PATH = "salt"; } diff --git a/app/src/main/java/com/stevesoltys/backup/transport/component/provider/ContentProviderRestoreComponent.java b/app/src/main/java/com/stevesoltys/backup/transport/component/provider/ContentProviderRestoreComponent.java index 98f06909..eb7841c5 100644 --- a/app/src/main/java/com/stevesoltys/backup/transport/component/provider/ContentProviderRestoreComponent.java +++ b/app/src/main/java/com/stevesoltys/backup/transport/component/provider/ContentProviderRestoreComponent.java @@ -9,6 +9,7 @@ import android.os.ParcelFileDescriptor; import android.util.Base64; import android.util.Log; import com.android.internal.util.Preconditions; +import com.stevesoltys.backup.security.CipherUtil; import com.stevesoltys.backup.security.KeyGenerator; import com.stevesoltys.backup.transport.component.RestoreComponent; import libcore.io.IoUtils; @@ -179,14 +180,11 @@ public class ContentProviderRestoreComponent implements RestoreComponent { private byte[] readBackupData(ZipInputStream inputStream) throws Exception { byte[] backupData = Streams.readFullyNoClose(inputStream); - if (configuration.getPassword() != null && !configuration.getPassword().isEmpty() && - restoreState.getSalt() != null) { + String password = configuration.getPassword(); + byte[] salt = restoreState.getSalt(); - SecretKey secretKey = KeyGenerator.generate(configuration.getPassword(), restoreState.getSalt()); - - Cipher cipher = Cipher.getInstance(ContentProviderBackupConstants.CIPHER_ALGORITHM); - cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(restoreState.getSalt())); - backupData = cipher.doFinal(backupData); + if (password != null && !password.isEmpty() && salt != null) { + backupData = CipherUtil.decrypt(backupData, password, salt); } return backupData;