I have been trying for two days to decrypt a file whith the private key containing 'lolilol' after having encrypted it with the public key rsa 4096. I then get padding errors, I tried everything to do, I get the error Javax.crypto.BadPaddingException: Decryption error.
Even after reading the doc on the padding I did not succeed: encryption works fine, but decryption contains an error.
Here is my code:
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.Security;
import javax.crypto.Cipher;
import sun.misc.BASE64Encoder;
public class GenerateRSAKeys{
private Key pubKey;
private Key privKey;
public static void main(String[] args)
{
String input = "C:\\Users\\toto\\Desktop\\nomFichier_entrant.ext";
String output = "C:\\Users\\toto\\Desktop\\nomFichier_entrant.ext.enc";
String dec = "C:\\Users\\toto\\Desktop\\nomFichier_entrant.ext.dec";
String publicKeyFilename = "C:\\Users\\toto\\Desktop\\HR_pubkey_prd.pem";
String privateKeyFilename = "C:\\Users\\toto\\Desktop\\PE_privkey_prd.pem";
GenerateRSAKeys generateRSAKeys = new GenerateRSAKeys();
/* if (args.length < 2)
{
System.err.println("Usage: java "+ generateRSAKeys.getClass().getName()+
" Public_Key_Filename Private_Key_Filename");
System.exit(1);
}
publicKeyFilename = args[0].trim();
privateKeyFilename = args[1].trim(); */
generateRSAKeys.generate(publicKeyFilename, privateKeyFilename);
//generateRSAKeys.encrypt(input, output);
generateRSAKeys.encrypt(input, output);
generateRSAKeys.decrypt(output, dec);
}
private void generate (String publicKeyFilename, String privateFilename){
try {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
// Create the public and private keys
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", "BC");
BASE64Encoder b64 = new BASE64Encoder();
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
/* SecureRandom random = new SecureRandom();
keyGen.initialize(4096, random);
KeyPair pair = keyGen.generateKeyPair();
pubKey = pair.getPublic();
privKey = pair.getPrivate(); */
SecureRandom random = createFixedRandom();
generator.initialize(4096, random);
KeyPair pair = generator.generateKeyPair();
pubKey = pair.getPublic();
privKey = pair.getPrivate();
System.out.println("publicKey : " + b64.encode(pubKey.getEncoded()));
System.out.println("privateKey : " + b64.encode(privKey.getEncoded()));
BufferedWriter out = new BufferedWriter(new FileWriter(publicKeyFilename));
out.write(b64.encode(pubKey.getEncoded()));
out.close();
out = new BufferedWriter(new FileWriter(privateFilename));
out.write(b64.encode(privKey.getEncoded()));
out.close();
}
catch (Exception e) {
System.out.println(e);
}
}
public static SecureRandom createFixedRandom()
{
return new FixedRand();
}
private static class FixedRand extends SecureRandom {
MessageDigest sha;
byte[] state;
FixedRand() {
try
{
this.sha = MessageDigest.getInstance("SHA-1");
this.state = sha.digest();
}
catch (NoSuchAlgorithmException e)
{
throw new RuntimeException("can't find SHA-1!");
}
}
public void nextBytes(byte[] bytes){
int off = 0;
sha.update(state);
while (off < bytes.length)
{
state = sha.digest();
if (bytes.length - off > state.length)
{
System.arraycopy(state, 0, bytes, off, state.length);
}
else
{
System.arraycopy(state, 0, bytes, off, bytes.length - off);
}
off += state.length;
sha.update(state);
}
}
}
public void encrypt(String input, String output) {
File outputFile;
FileInputStream inputStream;
FileOutputStream outputStream;
Cipher cipher;
byte[] inputBytes;
byte[] outputBytes;
try {
outputFile = new File(output);
inputStream = new FileInputStream(input);
outputStream = new FileOutputStream(outputFile);
cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
inputBytes = new byte[(int) input.length()];
inputStream.read(inputBytes);
outputBytes = cipher.doFinal(inputBytes);
outputStream.write(outputBytes);
// System.out.println(new String(inputBytes, "UTF-8"));
System.out.println("encrypt");
System.out.println(new String(outputBytes, "UTF-8"));
System.out.println("fin encrypt");
} catch (Exception e) {
e.printStackTrace();
}
}
public void decrypt(String input, String output) {
File outputFile;
FileInputStream inputStream;
FileOutputStream outputStream;
Cipher cipher;
byte[] inputBytes;
byte[] outputBytes;
try {
outputFile = new File(output);
inputStream = new FileInputStream(input);
outputStream = new FileOutputStream(outputFile);
cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privKey);
inputBytes = new byte[(int) input.length()];
inputStream.read(inputBytes);
outputBytes = cipher.doFinal(inputBytes);
outputStream.write(outputBytes);
// System.out.println(new String(inputBytes, "UTF-8"));
System.out.println("decrypt");
System.out.println(new String(outputBytes, "UTF-8"));
System.out.println("fin decrypt");
} catch (Exception e) {
e.printStackTrace();
}
}
}
You have multiple issues in your code:
You're never closing the file streams. If you don't do that, it might happen that no data is actually written. If the ciphertext is not (fully) written then the decryption will obviously fail.
A FileInputStream doesn't give an accurate measure of how much data the underlying file contains. You have to use the File class for that:
File inputFile = new File(input);
FileInputStream inputStream = new FileInputStream(inputFile);
FileOutputStream outputStream = new FileOutputStream(output);
byte[] inputBytes = new byte[(int) inputFile.length()];
When you're reading file contents, you must always check how much you read and use exactly that amount:
int readBytes = inputStream.read(inputBytes);
byte[] outputBytes = cipher.doFinal(inputBytes, 0, readBytes);
Always use a fully qualified Cipher string. Cipher.getInstance("RSA"); may result in different ciphers depending on the default security provider. In OpenJDK it defaults to Cipher.getInstance("RSA/ECB/PKCS1Padding");. Nowadays, you should use OAEP instead of the default PKCS#1 v1.5 padding. So you should probably use Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");.
Here is the full code of those two methods:
public void encrypt(String input, String output) {
File inputFile;
FileInputStream inputStream;
FileOutputStream outputStream;
Cipher cipher;
byte[] inputBytes;
byte[] outputBytes;
try {
System.out.println("encrypt");
cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
inputFile = new File(input);
inputStream = new FileInputStream(inputFile);
outputStream = new FileOutputStream(output);
inputBytes = new byte[(int) input.length()];
int readBytes = inputStream.read(inputBytes);
outputBytes = cipher.doFinal(inputBytes, 0, readBytes);
outputStream.write(outputBytes);
System.out.println("fin encrypt");
inputStream.close();
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public void decrypt(String input, String output) {
File inputFile;
FileInputStream inputStream;
FileOutputStream outputStream;
Cipher cipher;
byte[] inputBytes;
byte[] outputBytes;
try {
System.out.println("decrypt");
cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
cipher.init(Cipher.DECRYPT_MODE, privKey);
inputFile = new File(input);
inputStream = new FileInputStream(inputFile);
outputStream = new FileOutputStream(output);
inputBytes = new byte[(int) inputFile.length()];
int readBytes = inputStream.read(inputBytes);
outputBytes = cipher.doFinal(inputBytes, 0, readBytes);
outputStream.write(outputBytes);
System.out.println("Decryption result: " + new String(outputBytes, "UTF-8"));
System.out.println("fin decrypt");
inputStream.close();
outputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
Related
I am facing the following issue while working with Java cryptography.
error decrjavax.crypto.BadPaddingException: Given final block not
properly padded. Such issues can arise if a bad key is used during
decryption.
I have checked all possible answers, but couldn't find the exact reason behind this.
One observation that when i use AES/CBC/NoPadding in place of AES/CBC/PKCS5Padding, i can execute it successfully.
here is my code snippet.
package demo;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
public class TestEncryption {
private static final int BUFFER_SIZE = 32;
private static final int KEY_ITERATIONS = 65535;
private static final int DEFAULT_KEY_BITS = 128;
private static final String ALGORITHM = "AES/CBC/PKCS5Padding";
private static final String TRANSFORMATION = "AES";
private static final String PBKDF_2_WITH_HMAC_SHA_256 = "PBKDF2WithHmacSHA256";
private static final int IV_SIZE = 16;
private final Cipher ecipher;
private final Cipher dcipher;
private SecretKey secretKey;
/**
* Initialize the ciphers using the given key.
* #param key
* #param keyBits
*/
public TestEncryption(String key, int keyBits) {
byte[] salt = new byte[8];
if (key.length() < 8) {
throw new IllegalArgumentException("key must contain 8 characters or more");
}
for (int i = 0; i < 8; i = i + 1) {
salt[i] = ((byte) key.charAt(i));
}
char[] password = key.toCharArray();
int keyLength = DEFAULT_KEY_BITS;
try {
SecretKeyFactory factory = SecretKeyFactory.getInstance(PBKDF_2_WITH_HMAC_SHA_256);
if (keyBits == 256) {
keyLength = 256;
}
KeySpec spec = new PBEKeySpec(password, salt, KEY_ITERATIONS, keyLength);
secretKey = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), TRANSFORMATION);
ecipher = Cipher.getInstance(ALGORITHM);
dcipher = Cipher.getInstance(ALGORITHM);
} catch (InvalidKeySpecException | NoSuchPaddingException | NoSuchAlgorithmException e) {
throw new RuntimeException("Failed to initialize encryption.", e);
}
}
public void encryptFile(File src, File dest){
try {
InputStream inputStream = new FileInputStream(src);
OutputStream outputStream = new FileOutputStream(dest);
CipherOutputStream cipherOutputStream= new CipherOutputStream(outputStream, ecipher);
// Generating IV.
byte[] iv = new byte[IV_SIZE];
SecureRandom random = new SecureRandom();
random.nextBytes(iv);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
// First write the IV at the beginning of the encrypted file.
outputStream.write(iv, 0, IV_SIZE);
System.out.println("key " + secretKey);
// Initialize cipher with IV
ecipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec);
byte[] buffer = new byte[BUFFER_SIZE];
int bytesRead;
// Encrypt input file and write in to output
while ((bytesRead = inputStream.read(buffer)) > 0) {
cipherOutputStream.write(buffer, 0, bytesRead);
}
} catch (InvalidKeyException | InvalidAlgorithmParameterException | IOException e) {
System.out.println("error encryption" + e.getMessage());
e.printStackTrace();
}
}
public void decryptFile(File srcFile, File destFile) {
try (
InputStream is = new FileInputStream(srcFile);
OutputStream out = new FileOutputStream(destFile);
CipherInputStream cis = new CipherInputStream(is, dcipher)
) {
// Extract IV
byte[] iv = new byte[IV_SIZE];
is.read(iv, 0, IV_SIZE);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
// Initialize cypher with IV
dcipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
byte[] buffer = new byte[BUFFER_SIZE];
int bytesRead;
while ((bytesRead = cis.read(buffer)) > 0) {
out.write(buffer, 0, bytesRead);
}
} catch ( InvalidKeyException | InvalidAlgorithmParameterException | IOException e) {
System.out.println("error decr" + e.getMessage());
e.printStackTrace();
}
}
}
package demo;
import java.io.*;
public class Client {
public static void main(String [] args){
File tempFile =null, src = null, dest = null;
try {
tempFile = new File("temp.txt");
src = new File("C:\\Users\\x\\Desktop\\test.txt");
dest = new File("C:\\Users\\x\\Desktop\\out.txt");
TestEncryption encryption = new TestEncryption("helloworld", 256);
encryption.encryptFile(src, tempFile);
encryption.decryptFile(tempFile, dest);
}
finally {
tempFile.delete();
//src.delete();
//dest.delete();
}
}
}
Your error is the way to use your streams when encrypting:
For a CipherOutputStream it is essential to be closed at the end because only when it is closed the final padding can be written.
In your code however the cipherOutputStream instance is never closed. hence the padding is never written to the encrypted file.
Of course when decrypting the file there is no padding where a padding should be and you are getting the BadPaddingException.
Therefore you should change the encyrption to this:
public void encryptFile(File src, File dest) {
try (InputStream inputStream = new FileInputStream(src);
OutputStream outputStream = new FileOutputStream(dest)) {
try (CipherOutputStream cipherOutputStream = new CipherOutputStream(outputStream, ecipher)) {
// Generating IV.
byte[] iv = new byte[IV_SIZE];
SecureRandom random = new SecureRandom();
random.nextBytes(iv);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
// First write the IV at the beginning of the encrypted file.
outputStream.write(iv, 0, IV_SIZE);
System.out.println("key 0x" + new BigInteger(1, secretKey.getEncoded()).toString(16));
// Initialize cipher with IV
ecipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec);
byte[] buffer = new byte[BUFFER_SIZE];
int bytesRead;
// Encrypt input file and write in to output
while ((bytesRead = inputStream.read(buffer)) >= 0) {
cipherOutputStream.write(buffer, 0, bytesRead);
}
}
} catch (InvalidKeyException | InvalidAlgorithmParameterException | IOException e) {
System.out.println("error encryption" + e.getMessage());
e.printStackTrace();
}
}
public void decryptFile(File srcFile, File destFile) {
try (InputStream is = new FileInputStream(srcFile); OutputStream out = new FileOutputStream(destFile)) {
try (CipherInputStream cis = new CipherInputStream(is, dcipher)) {
// Extract IV
byte[] iv = is.readNBytes(IV_SIZE);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
// Initialize cypher with IV
dcipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
byte[] buffer = new byte[BUFFER_SIZE];
int bytesRead;
while ((bytesRead = cis.read(buffer)) >= 0) {
out.write(buffer, 0, bytesRead);
}
}
} catch (InvalidKeyException | InvalidAlgorithmParameterException | IOException e) {
System.out.println("error decr" + e.getMessage());
e.printStackTrace();
}
}
There is another reason that probably the content which you need to description, but the arguments is null when passed to the mathod, it will be throw you a badpaddedException as well.
I wanted to try out the code from Java In A Nutshell book (3rd edition), but when I'm trying to run it, I'm getting java.lang.ArrayIndexOutOfBoundsException: 1 error. I found that info:
Thrown to indicate that an array has been accessed with an illegal index. The index is either negative or greater than or equal to the size of the array.
but unfortunately I am still not able to find and fix it
package tripledes;
import javax.crypto.*;
import javax.crypto.spec.*;
import java.security.*;
import java.security.spec.*;
import java.io.*;
public class TripleDES {
public static void main(String[] args) {
try {
try {
Cipher c = Cipher.getInstance("DESede");
} catch (Exception e) {
System.err.println("Installing SunJCE provicer");
Provider sunjce = new com.sun.crypto.provider.SunJCE();
Security.addProvider(sunjce);
}
File keyfile = new File(args[1]);
if (args[0].equals("-g")) {
System.out.println("Generating key. This may take some time...");
System.out.flush();
SecretKey key = generateKey();
writeKey(key, keyfile);
System.out.println("Done");
System.out.println("Secret key written to " + args[1] + ". Protect that file!");
} else if (args[0].equals("-e")) {
SecretKey key = readKey(keyfile);
encrypt(key, System.in, System.out);
} else if (args[0].equals("-d")) {
SecretKey key = readKey(keyfile);
decrypt(key, System.in, System.out);
}
} catch (Exception e) {
System.err.println(e);
System.err.println("Usage: java " + TripleDES.class.getName() + "-d|-e|-g <keyfile>");
}
}
public static SecretKey generateKey() throws NoSuchAlgorithmException {
KeyGenerator keygen = KeyGenerator.getInstance("DESede");
return keygen.generateKey();
}
public static void writeKey(SecretKey key, File f) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
SecretKeyFactory keyfactory = SecretKeyFactory.getInstance("DESede");
DESedeKeySpec keyspec = (DESedeKeySpec) keyfactory.getKeySpec(key, DESedeKeySpec.class);
byte[] rawkey = keyspec.getKey();
FileOutputStream out = new FileOutputStream(f);
out.write(rawkey);
out.close();
}
public static SecretKey readKey(File f) throws IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidKeySpecException {
DataInputStream in = new DataInputStream(new FileInputStream(f));
byte[] rawkey = new byte[(int) f.length()];
in.readFully(rawkey);
in.close();
DESedeKeySpec keyspec = new DESedeKeySpec(rawkey);
SecretKeyFactory keyfactory = SecretKeyFactory.getInstance("DESede");
SecretKey key = keyfactory.generateSecret(keyspec);
return key;
}
public static void encrypt(SecretKey key, InputStream in, OutputStream out)
throws NoSuchAlgorithmException, InvalidKeyException, NoSuchPaddingException, IOException {
Cipher cipher = Cipher.getInstance("DESede");
cipher.init(Cipher.ENCRYPT_MODE, key);
CipherOutputStream cos = new CipherOutputStream(out, cipher);
byte[] buffer = new byte[2048];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
cos.write(buffer, 0, bytesRead);
}
cos.close();
java.util.Arrays.fill(buffer, (byte) 0);
}
public static void decrypt(SecretKey key, InputStream in, OutputStream out)
throws NoSuchAlgorithmException, InvalidKeyException, IOException, IllegalBlockSizeException,
NoSuchPaddingException, BadPaddingException {
Cipher cipher = Cipher.getInstance("DESede");
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] buffer = new byte[2048];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
out.write(cipher.update(buffer, 0, bytesRead));
}
out.write(cipher.doFinal());
out.flush();
}
}
Could be when you access the args array. Such as:
File keyfile = new File(args[1]);
If you have no arguments to your program then this would be an ArrayIndexOutOfBoundsException.
PROBLEM: Upload method is working the problem I'm presenting is **encrypt(inputFile, new File("Users/myname/Desktop/example.txt"),key);** instead of writing to my desktop I want it to be stored as a file or var I can use so I can upload to dropbox server. I just do not know how to code this part and do not know what to put so this will execute appropriately.
private byte[] getKeyBytes(final byte[] key) throws Exception {
byte[] keyBytes = new byte[16];
System.arraycopy(key, 0, keyBytes, 0, Math.min(key.length, keyBytes.length));
return keyBytes;
}
public Cipher getCipherEncrypt(final byte[] key) throws Exception {
byte[] keyBytes = getKeyBytes(key);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(keyBytes);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
return cipher;
}
public void encrypt(File in, File output, byte[] key) throws Exception {
Cipher cipher = getCipherEncrypt(key);
FileOutputStream fos = null;
CipherOutputStream cos = null;
FileInputStream fis = null;
try {
fis = new FileInputStream(in);
fos = new FileOutputStream(output);
cos = new CipherOutputStream(fos, cipher);
byte[] data = new byte[1024];
int read = fis.read(data);
while (read != -1) {
cos.write(data, 0, read);
read = fis.read(data);
System.out.println(new String(data, "UTF-8").trim());
}
cos.flush();
System.out.println("name"+ output);
} finally {
System.out.println("performed encrypt method now closing streams");
cos.close();
fos.close();
fis.close();
}
}
// Working Upload Method To DropBox Cloud
public void uploadFile () throws DbxException, IOException, FileLoadException, NoSuchAlgorithmException, InvalidKeyException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException, InvalidKeySpecException, InvalidParameterSpecException, ClassNotFoundException, NoSuchProviderException, InvalidAlgorithmParameterException, UnsupportedAudioFileException, LineUnavailableException, InterruptedException, ShortBufferException, Exception {
// autheticate if there is a accessToken here if not prompt to login by activating the drop method re-auth..
try{
phoneHome();
}catch(IOException e){
System.out.println("not saving accessToken");
JOptionPane.showMessageDialog(null, "Your Access Information Does Not Exist,\n Please Login"+
"Please Login By Clicking 'OK'");
drop(); // will run auth method for user to login
}
// user picks file to upload with JFileChooser
fc = new JFileChooser();
fc.setMultiSelectionEnabled(true);
fc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
int dialog = fc.showSaveDialog(this);
if (dialog == JFileChooser.APPROVE_OPTION) {
inputFile = fc.getSelectedFile();
inputFile.getName();
inputFile.getAbsoluteFile();
String nameOf = inputFile.getName();
System.out.println(" File: " + inputFile);
/* calling out inputFIle from out JFileChooser and now passing it into
our method and encrytping and outputing it.
*/
encrypt(inputFile, new File("Users/myname/Desktop/example.txt"),key);
File selected = new File ("?");
inputStream = new FileInputStream (selected);
uploadedFile = client.uploadFile( "/" +selected ,DbxWriteMode.add(), inputFile.length(), inputStream);
/*
Original works without encryption
File selectedFile = new File(nameOf+inputFile);
inputStream = new FileInputStream(inputFile);
*/
// Original uploadedFile = client.uploadFile( "/" +selectedFile ,DbxWriteMode.add(), inputFile.length(), inputStream);
setTitle("Uploading File..");
System.out.println("Uploaded: " + uploadedFile.toString());
JOptionPane.showMessageDialog(null,"File Upload:" + uploadedFile.toString(),
"Success!", JOptionPane.WARNING_MESSAGE);
}
}
Solved... all it was is simply over complicating something that didn't need to be complicated furthermore and have settled for a more simplicit way.
ORIGINAL: encrypt(inputFile, new File("Users/myname/Desktop/example.txt"),key);
WORKING: encrypt(inputFile, new File(inputFile.getName),key);
Eazy Peezy... if anyone has any other suggestions I would like to hear them, thanks.
I save and load in sd card a file that contains an ArrayList of serializable object with these two methods
save method
public static void saveUserList(ArrayList<User> userList) {
if (storageAvailable()) {
try {
createFolder();
FileOutputStream userList = new FileOutputStream(
baseDir + File.separator + baseAppDir + File.separator
+ fileName);
ObjectOutputStream oos = new ObjectOutputStream(
userList);
oos.writeObject(userList);
oos.close();
} catch (Exception exc) {
exc.printStackTrace();
}
}
}
load method
public static ArrayList<User> loadUserList() {
if (storageAvailable()) {
ArrayList<User> userList = new ArrayList<User>();
try {
FileInputStream userList = new FileInputStream(baseDir
+ File.separator + baseAppDir + File.separator
+ fileName);
ObjectInputStream oos = new ObjectInputStream(
userList);
userList = (ArrayList<User>) oos.readObject();
oos.close();
} catch (Exception exc) {
exc.printStackTrace();
}
return userList;
} else {
return null;
}
}
Now I want that the method saveUserList encrypts the content of the file during the save according a specific String keyword and the method loadUserList decrypts the file with the same keyword to return the arrayList.
How could I do this?
I have given a look to CipherOutputStream but I haven't understood how should I use this.
The method proposed to use Conceal library
public static void saveUserListCrypted(ArrayList<User> userList) {
if (storageAvailable()) {
try {
createFolder();
Crypto crypto = new Crypto(
new SharedPrefsBackedKeyChain(context),
new SystemNativeCryptoLibrary());
FileOutputStream userList = new FileOutputStream(
baseDir + File.separator + baseAppDir + File.separator
+ fileName);
OutputStream cryptedStream = crypto.getCipherOutputStream(
userList, new Entity("UserList");
ObjectOutputStream oos = new ObjectOutputStream(
cryptedStream);
oos.writeObject(userList);
oos.close();
} catch (Exception exc) {
exc.printStackTrace();
}
}
}
cause this error
this error java.lang.UnsupportedOperationException 02-12 21:29:05.026 2051-2051/com.myapp W/System.errīš at com.facebook.crypto.streams.NativeGCMCipherOutputStream.write
Try (adding the appropriate checks and try blocks that I have omitted to make the code more readable) something like this to save
public static void AESObjectEncoder(Serializable object, String password, String path) {
try {
Cipher cipher = null;
cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
cipher.init(Cipher.ENCRYPT_MODE, fromStringToAESkey(password));
SealedObject sealedObject = null;
sealedObject = new SealedObject(object, cipher);
CipherOutputStream cipherOutputStream = null;
cipherOutputStream = new CipherOutputStream(new BufferedOutputStream(new FileOutputStream(path)), cipher);
ObjectOutputStream outputStream = null;
outputStream = new ObjectOutputStream(cipherOutputStream);
outputStream.writeObject(sealedObject);
outputStream.close();
}
and this to load
public static Serializable AESObjectDedcoder(String password, String path) {
Cipher cipher = null;
Serializable userList = null;
cipher = Cipher.getInstance("AES/CBC/PKCS7Pdding");
//Code to write your object to file
cipher.init(Cipher.DECRYPT_MODE, fromStringToAESkey(password));
CipherInputStream cipherInputStream = null;
cipherInputStream = new CipherInputStream(new BufferedInputStream(new FileInputStream(path)), cipher);
ObjectInputStream inputStream = null;
inputStream = new ObjectInputStream(cipherInputStream);
SealedObject sealedObject = null;
sealedObject = (SealedObject) inputStream.readObject();
userList = (Serializable) sealedObject.getObject(ciper);
return userList;
}
to create a SecretKey from a String you can use this
public static SecretKey fromStringToAESkey(String s) {
//256bit key need 32 byte
byte[] rawKey = new byte[32];
// if you don't specify the encoding you might get weird results
byte[] keyBytes = new byte[0];
try {
keyBytes = s.getBytes("ASCII");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
System.arraycopy(keyBytes, 0, rawKey, 0, keyBytes.length);
SecretKey key = new SecretKeySpec(rawKey, "AES");
return key;
}
NOTE:
this code encrypts and decrypts twice to show the way of use of both sealed object and Cipher streams
I suggest taking a look at Conceal, recently released by facebook: http://facebook.github.io/conceal/
This should be a trivial modification to wrap a Conceal output stream with an ObjectOutputStream used in your current code:
public static void saveUserList(ArrayList<User> userList) {
if (storageAvailable()) {
try {
createFolder();
Crypto crypto = new Crypto(
new SharedPrefsBackedKeyChain(context),
new SystemNativeCryptoLibrary());
FileOutputStream userList = new FileOutputStream(
baseDir + File.separator + baseAppDir + File.separator
+ fileName);
OutputStream cryptedStream = crypto.getCipherOutputStream(
userList, new Entity("UserList");
ObjectOutputStream oos = new ObjectOutputStream(
cryptedStream);
oos.writeObject(userList);
oos.close();
} catch (Exception exc) {
exc.printStackTrace();
}
}
}
I'll leave the restore as an exercise for the reader. ;)
You could simply use AES Encoding:
private static byte[] getEncrypt(final String key, final String message) throws GeneralSecurityException {
final byte[] rawData = key.getBytes(Charset.forName("US-ASCII"));
if (rawData.length != 16) {
// If this is not 16 in length, there's a problem with the key size, nothing to do here
throw new IllegalArgumentException("You've provided an invalid key size");
}
final SecretKeySpec seckeySpec = new SecretKeySpec(rawData, "AES");
final Cipher ciph = Cipher.getInstance("AES/CBC/PKCS5Padding");
ciph.init(Cipher.ENCRYPT_MODE, seckeySpec, new IvParameterSpec(new byte[16]));
return ciph.doFinal(message.getBytes(Charset.forName("US-ASCII")));
}
private static String getDecrypt(String key, byte[] encrypted) throws GeneralSecurityException {
final byte[] rawData = key.getBytes(Charset.forName("US-ASCII"));
if (rawData.length != 16) {
// If this is not 16 in length, there's a problem with the key size, nothing to do here
throw new IllegalArgumentException("Invalid key size.");
}
final SecretKeySpec seckeySpec = new SecretKeySpec(rawData, "AES");
final Cipher ciph = Cipher.getInstance("AES/CBC/PKCS5Padding");
ciph.init(Cipher.DECRYPT_MODE, seckeySpec, new IvParameterSpec(new byte[16]));
final byte[] decryptedmess = ciph.doFinal(encrypted);
return new String(decryptedmess, Charset.forName("US-ASCII"));
}
Then, for trying it, this example may be valid for you:
final String encrypt = "My16inLengthKey5";
final byte[] docrypt = getEncrypt(encrypt, "This is a phrase to be encrypted!");
final String douncrypt = getDecrypt(encrypt.toString(), docrypt);
Log.d("Decryption", "Decrypted phrase: " + douncrypt);
Of course, douncrypt must match This is a phrase to be encrypted!
I have ported an application from Android to desktop that uses AES to encrypt some private data.
Both applications are able to encrypt and decrypt the data for their own use but unable to decrypt the other applications data.
The AES keys, IVs, and algorithms are identical.
The main difference between the two applications is that the android-sdk comes with the BouncyCastle provider already added to the security while the desktop application needed
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
Android app:
public class AesFileIo {
public final static String EOL = "\n";
public static final String AES_ALGORITHM = "AES/CTR/NoPadding";
public static final String PROVIDER = "BC";
private static final SecretKeySpec secretKeySpec =
new SecretKeySpec(AES_KEY_128, "AES");
private static final IvParameterSpec ivSpec = new IvParameterSpec(IV);
public String readAesFile(Context c, String fileName) {
StringBuilder stringBuilder = new StringBuilder();
try {
InputStream is = c.openFileInput(fileName);
Cipher cipher = Cipher.getInstance(AES_ALGORITHM, PROVIDER);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivSpec);
CipherInputStream cis = new CipherInputStream(is, cipher);
InputStreamReader isr = new InputStreamReader(cis);
BufferedReader reader = new BufferedReader(isr);
String line;
while ((line = reader.readLine()) != null) {
stringBuilder.append(line).append(EOL);
}
is.close();
} catch (java.io.FileNotFoundException e) {
// OK, file probably not created yet
Log.i(this.getClass().toString(), e.getMessage(), e);
} catch (Exception e) {
Log.e(this.getClass().toString(), e.getMessage(), e);
}
return stringBuilder.toString();
}
public void writeAesFile(Context c, String fileName, String theFile) {
try {
Cipher cipher = Cipher.getInstance(AES_ALGORITHM, PROVIDER);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivSpec);
byte[] encrypted = cipher.doFinal(theFile.getBytes());
OutputStream os = c.openFileOutput(fileName, 0);
os.write(encrypted);
os.flush();
os.close();
} catch (Exception e) {
Log.e(this.getClass().toString(), e.getMessage(), e);
}
}
}
Desktop app:
public class AesFileIo {
private static final String EOL = "\n";
private static final String AES_ALGORITHM = "AES/CTR/NoPadding";
private static final SecretKeySpec secretKeySpec =
new SecretKeySpec(AES_KEY_128, "AES");
private static final IvParameterSpec ivSpec = new IvParameterSpec(IV);
public void AesFileIo() {
Security.addProvider(new org.bouncycastle.jce.provider
.BouncyCastleProvider());
}
public String readFile(String fileName) {
StringBuilder stringBuilder = new StringBuilder();
try {
ObjectInputStream is = new ObjectInputStream(
new FileInputStream(fileName));
Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivSpec);
CipherInputStream cis = new CipherInputStream(is, cipher);
InputStreamReader isr = new InputStreamReader(cis);
BufferedReader reader = new BufferedReader(isr);
String line;
while ((line = reader.readLine()) != null) {
stringBuilder.append(line).append(EOL);
}
is.close();
} catch (java.io.FileNotFoundException e) {
System.out.println("FileNotFoundException: probably OK");
} catch (Exception e) {
e.printStackTrace();
}
return stringBuilder.toString();
}
public void writeFile(String fileName, String theFile) {
try {
Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivSpec);
byte[] encrypted = cipher.doFinal(theFile.getBytes());
ObjectOutputStream os = new ObjectOutputStream(
new FileOutputStream(fileName));
os.write(encrypted);
os.flush();
os.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Solved by
Adding proper constructors to initialize SecretKeySpec and IvParameterSpec.
Getting rid of ObjectOutputStream and ObjectInputStream in desktop app.
Android app:
public class AesFileIo {
private static final String EOL = "\n";
private static final String AES_ALGORITHM = "AES/CTR/NoPadding";
private SecretKeySpec secretKeySpec;
private IvParameterSpec ivSpec;
private static final String PROVIDER = "BC";
AesFileIo(byte[] aesKey, byte[] iv) {
ivSpec = new IvParameterSpec(iv);
secretKeySpec = new SecretKeySpec(aesKey, "AES");
}
public String readFile(Context c, String fileName) {
StringBuilder stringBuilder = new StringBuilder();
try {
InputStream is = c.openFileInput(fileName);
Cipher cipher = Cipher.getInstance(AES_ALGORITHM, PROVIDER);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivSpec);
CipherInputStream cis = new CipherInputStream(is, cipher);
InputStreamReader isr = new InputStreamReader(cis);
BufferedReader reader = new BufferedReader(isr);
String line;
while ((line = reader.readLine()) != null) {
stringBuilder.append(line).append(EOL);
}
is.close();
} catch (java.io.FileNotFoundException e) {
// OK, file probably not created yet
Log.i(this.getClass().toString(), e.getMessage(), e);
} catch (Exception e) {
Log.e(this.getClass().toString(), e.getMessage(), e);
}
return stringBuilder.toString();
}
public void writeFile(Context c, String fileName, String theFile) {
try {
Cipher cipher = Cipher.getInstance(AES_ALGORITHM, PROVIDER);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivSpec);
byte[] encrypted = cipher.doFinal(theFile.getBytes());
OutputStream os = c.openFileOutput(fileName, 0);
os.write(encrypted);
os.flush();
os.close();
} catch (Exception e) {
Log.e(this.getClass().toString(), e.getMessage(), e);
}
}
}
Desktop app:
public class AesFileIo {
private static final String EOL = "\n";
private static final String AES_ALGORITHM = "AES/CTR/NoPadding";
private SecretKeySpec secretKeySpec;
private IvParameterSpec ivSpec;
AesFileIo(byte[] aesKey, byte[] iv) {
Security.addProvider(new org.bouncycastle.jce.provider
.BouncyCastleProvider());
ivSpec = new IvParameterSpec(iv);
secretKeySpec = new SecretKeySpec(aesKey, "AES");
}
public String readFile(String fileName) {
StringBuilder stringBuilder = new StringBuilder();
try {
FileInputStream fis = new FileInputStream(fileName);
Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivSpec);
CipherInputStream cis = new CipherInputStream(fis, cipher);
InputStreamReader isr = new InputStreamReader(cis);
BufferedReader reader = new BufferedReader(isr);
String line;
while ((line = reader.readLine()) != null) {
stringBuilder.append(line).append(EOL);
}
fis.close();
} catch (java.io.FileNotFoundException e) {
System.out.println("FileNotFoundException: probably OK");
} catch (Exception e) {
e.printStackTrace();
}
return stringBuilder.toString();
}
public void writeFile(String fileName, String theFile) {
try {
Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivSpec);
byte[] encrypted = cipher.doFinal(theFile.getBytes());
FileOutputStream fos = new FileOutputStream(fileName);
fos.write(encrypted);
fos.flush();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}