To secure app storage, I encrypted the storage using EncryptedStorage
as explained here. That is,
EncryptedStorage.install("your-pass-encryption-key")
I created a text file to be shared and opened/used by the same app in another device.
The problem is that this file can't be open/used in another device. It says that this file is encrypted.
How can I decrypt it to be able to open or use it in the same app in another device?
EDIT
I did the following to create the file to share as a .txt file so as to be able to share it using plain/text
mime type.
String databasePath = Display.getInstance().getDatabasePath("TestDB.db");
String mimeType = "application/vnd.sqlite3";
mimeType = "application/x-sqlite3";
mimeType = "plain/text";
try {
FileSystemStorage fss = FileSystemStorage.getInstance();
InputStream is = fss.openInputStream(databasePath);
//Copy from FSS to Storage
Util.copy(is, Storage.getInstance().createOutputStream("TestDB.txt"));
String fileToSharePath = FileSystemStorage.getInstance().getAppHomePath() + "TestDB.txt";
Log.p("FileToSharePath " + fileToSharePath);
Display.getInstance().share(fileToSharePath, null, mimeType);
} catch (IOException e) {
Log.p("Share error: " + e);
}
I did the following to browse the shared file using FileChooser
in another device and save it as .db file in the app database directory.
FileChooser.showOpenDialog("txt", ev -> {
if (ev == null) {
Log.p("no file selected");
} else {
String filePath = (String) ev.getSource();
Log.p("FilePath " + filePath);
try {
FileSystemStorage fss = FileSystemStorage.getInstance();
InputStream is = fss.openInputStream(filePath);
String databasePath = Display.getInstance().getDatabasePath("TestDB.db");
OutputStream os = FileSystemStorage.getInstance().openOutputStream(databasePath);
Util.copy(is, os);
} catch (IOException e) {
Log.p("Create DB error: " + e);
}
}
});
The following creates a encrypted SampleText.txt
to app storage which can't be read when open in text editor. It can only be read by the app.
EncryptedStorage.install("your-pass-encryption-key")
private void writeAndReadCipherFile() {
try {
//write encrypted file to the app storage
try (Writer w = new OutputStreamWriter(Storage.getInstance().createOutputStream("SampleText.txt"))) {
w.write("Hey there big brother");
} catch (Exception e) {
Log.p("Error " + e);
}
//read encrypted file from the app storage
InputStream is = Storage.getInstance().createInputStream("SampleText.txt");
int nextChar = is.read();
StringBuilder sb = new StringBuilder();
while (nextChar > -1) {
sb.append((char) nextChar);
nextChar = is.read();
}
Log.p("FileContent " + sb.toString());
} catch (Exception e) {
Log.p("Error " + e);
}
}
The following reads and encrypt database file content using BouncyCastle
and save encrypted database file in the app home path directory home/.cn1
It also read encrypted file content, decrypts and save the decrypted file in home/.cn1
.
ISO-8859-1
encoding is required in OutputStreamWriter
to ensure all characters in the file bytes are read as expected.
Windows-1252
encoding also tries but some hexadecimal are read as ?
.
private void encryptDatabaseFile() {
try {
String filePath = Display.getInstance().getDatabasePath("TestDB.db");
FileSystemStorage fss = FileSystemStorage.getInstance();
InputStream is = fss.openInputStream(filePath);
byte[] plainBytes = Util.readInputStream(is);
//encrypt database file content
byte[] cipherByteArray = FilesCipher.encryptFile(plainBytes);
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(cipherByteArray);
String encryptedFilePath = fss.getAppHomePath() + "EncryptedDB.db";
try (Writer w = new OutputStreamWriter(fss.openOutputStream(encryptedFilePath), "ISO-8859-1")) {//ISO-8859-1 Windows-1252 UTF-8
int nextChar = byteArrayInputStream.read();
while (nextChar > -1) {
char[] charArray = {(char) nextChar};
w.write(charArray);
nextChar = byteArrayInputStream.read();
}
} catch (Exception e) {
Log.p("Error " + e);
}
//decrypt database file content
InputStream decryptInputStream = fss.openInputStream(encryptedFilePath);
byte[] cipherBytes = Util.readInputStream(decryptInputStream);
byte[] plainByteArray = FilesCipher.decryptFile(cipherBytes);
ByteArrayInputStream decryptedInputStream = new ByteArrayInputStream(plainByteArray);
String decryptedFilePath = fss.getAppHomePath() + "DecryptedDB.db";
try (Writer w = new OutputStreamWriter(fss.openOutputStream(decryptedFilePath), "ISO-8859-1")) {//ISO-8859-1 Windows-1252 UTF-8
int nextChar = decryptedInputStream.read();
while (nextChar > -1) {
char[] charArray = {(char) nextChar};
w.write(charArray);
nextChar = decryptedInputStream.read();
}
} catch (Exception e) {
Log.p("Error " + e);
}
} catch (Exception e) {
Log.p("Error " + e);
}
}
The following FilesCipher
class handles encryption and decryption
import com.codename1.io.Log;
import java.io.UnsupportedEncodingException;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.modes.CBCBlockCipher;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
import org.bouncycastle.util.encoders.Base64;
public class FilesCipher {
private static PaddedBufferedBlockCipher cipher;
private static final String SECRET_KEY = "Your 16 Character Secret Key";
private static final String I_VECTOR = "Your 16 Character initialization vector";
public static byte[] encryptFile(byte[] plainBytes) {
byte[] byteArray = null;
try {
cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine()));
byte sKey[] = SECRET_KEY.getBytes("UTF-8");
byte[] iv = I_VECTOR.getBytes("UTF-8");
KeyParameter keyParam = new KeyParameter(sKey);
CipherParameters ivAndKey = new ParametersWithIV(keyParam, iv);
cipher.init(true, ivAndKey);
byte[] cipherBytes = cipherData(plainBytes);
byteArray = Base64.encode(cipherBytes);
} catch (UnsupportedEncodingException | IllegalArgumentException
| CryptoException e) {
Log.p("Error " + e);
}
return byteArray;
}
public static byte[] decryptFile(byte[] cipherBytes) {
byte[] byteArray = null;
try {
cipher = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine()));
byte sKey[] = SECRET_KEY.getBytes("UTF-8");
byte[] iv = I_VECTOR.getBytes("UTF-8");
KeyParameter keyParam = new KeyParameter(sKey);
byte[] cipherData = Base64.decode(cipherBytes);
CipherParameters ivAndKey = new ParametersWithIV(keyParam, iv);
cipher.init(false, ivAndKey);
byteArray = cipherData(cipherData);
} catch (UnsupportedEncodingException | IllegalArgumentException
| CryptoException e) {
Log.p("Error " + e);
}
return byteArray;
}
private static byte[] cipherData(byte[] data) throws CryptoException {
int minSize = cipher.getOutputSize(data.length);
byte[] outBuf = new byte[minSize];
int len1 = cipher.processBytes(data, 0, data.length, outBuf, 0);
int len2 = cipher.doFinal(outBuf, len1);
int actualLen = len1 + len2;
byte[] result = new byte[actualLen];
System.arraycopy(outBuf, 0, result, 0, result.length);
return result;
}
}
This also makes it possible to keep the database encrypted. That is, after database is created, this database file can be encrypted using FilesCipher.encryptFile()
then save this as the database.
Before executing Create, Read, Update & Delete
queries, decrypt the database file using FilesCipher.decryptFile()
.
After executing queries, encrypt the database again using FilesCipher.encryptFile()