Encrypting and decrypting List<String> in java - java

I have an mobile app and a desktop app.I have multiple list in desktop app. I want to encrypt all the values in the lists and send to a file and later from the mobile app i want retrieve the data from the file and decrypt the values and display them. I am using the encryption and decryption concept for the first time.I tried sending a string by encrypting and it worked.But i want to encrypt many list . How will i do that.Any code will be helpful.
For encrypting:
KeyGenerator keygenerator = KeyGenerator.getInstance("DES");
SecretKey myDesKey = keygenerator.generateKey();
Cipher desCipher;
desCipher = Cipher.getInstance("DES");
byte[] text = "Hello".getBytes("UTF8");
desCipher.init(Cipher.ENCRYPT_MODE, myDesKey);
byte[] textEncrypted = desCipher.doFinal(text);
String s = new String(textEncrypted);
System.out.println(s);
For Decrpyting
desCipher.init(Cipher.DECRYPT_MODE, myDesKey);
byte[] textDecrypted = desCipher.doFinal(textEncrypted);
s = new String(textDecrypted);
System.out.println(s);
I used this code for a string but How to achieve the same with the list.Please help.
Thanks in advance.

you can convert your list to byte array by
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(list);
byte[] text = bos.toByteArray();
then encrypt text as general. Then you can convert decrypted byte array to list as
ByteArrayInputStream bis = new ByteArrayInputStream(textDecrypted);
ObjectInputStream ois = new ObjectInputStream(bis);
List<String> result = (List<String>) ois.readObject();
Example:
List<String> list = new ArrayList<String>();
list.add("Hello");
list.add(" World!!");
System.out.println(list);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(list);
byte[] text = bos.toByteArray();
KeyGenerator keygenerator = KeyGenerator.getInstance("DES");
SecretKey myDesKey = keygenerator.generateKey();
Cipher desCipher;
desCipher = Cipher.getInstance("DES");
desCipher.init(Cipher.ENCRYPT_MODE, myDesKey);
byte[] textEncrypted = desCipher.doFinal(text);
desCipher.init(Cipher.DECRYPT_MODE, myDesKey);
byte[] textDecrypted = desCipher.doFinal(textEncrypted);
ByteArrayInputStream bis = new ByteArrayInputStream(textDecrypted);
ObjectInputStream ois = new ObjectInputStream(bis);
List<String> result = (List<String>) ois.readObject();
System.out.println(result);

You can use ArrayList and add every value in this list.
List<String> list = new ArrayList<>();
for () {
// here first you encrypt the data then add to the list
}
Save it to file.
And then when you retrieve you again put then in the list, and then:
for(String str: list) {
// do decryption
}

Related

How to encrypt Credentials object - Cipher.doFinal, SealedObject, or CipherOutputStream?

I need to encrypt a set of user credentials and send it to a SOAP web service. The following code snippet (I think it's C#) is provided in the documentation, and my Java code is based on it.
private string Encrypt256(string text, AesCryptoServiceProvider aes)
{
// Convert string to byte array
byte[] src = Encoding.Unicode.GetBytes(text);
// encryption
using (ICryptoTransform encrypt = aes.CreateEncryptor())
{
byte[] dest = encrypt.TransformFinalBlock(src, 0, src.Length);
// Convert byte array to Base64 strings
return Convert.ToBase64String(dest);
}
}
...
Credentials credential = new Credentials();
credential.UserName = "username";
credential.Password = "password";
credential.ClientUtcTime = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ", System.Globalization.CultureInfo.InvariantCulture);
//--Serialize credential
XmlSerializer serializer = new XmlSerializer(credential.GetType());
string xmlCredential = string.Empty;
using (var stringwriter = new System.IO.StringWriter())
{
serializer.Serialize(stringwriter, credential);
xmlCredential = stringwriter.ToString();
}
//--Encrypt credential with AES256 symmetric
String encryptedCredential = Encrypt256(xmlCredential, aesServiceProvider);
...
The following is my Java code.
KeyGenerator kg = KeyGenerator.getInstance("AES");
kg.init(256);
SecretKey sk = kg.generateKey();
Cipher aesCipher = Cipher.getInstance("AES");
aesCipher.init(Cipher.ENCRYPT_MODE, sk);
Credentials cred = new UsernamePasswordCredentials("username", "password");//no need for time field?
String eCred = Base64.encodeBase64String(aesCipher.doFinal(objectToByteArray(cred)));
...
private byte[] objectToByteArray(Object obj) {
byte[] bytes = null;
try (
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
) {
oos.writeObject(obj);
oos.flush();
bytes = bos.toByteArray();
}
catch (IOException e) {
e.printStackTrace();
}
return bytes;
}
Then I came across SealedObject and CipherOutputStream. I tried writing code snippets for those.
Using SealedObject
// slight change here; cred must implement Serializable
UsernamePasswordCredentials cred = new UsernamePasswordCredentials("username", "password");
// same as above except for the following two lines
SealedObject so = new SealedObject(cred, aesCipher);
String eCred = Base64.encodeBase64String(objectToByteArray(so));
Using CipherOutputStream
Credentials cred = new UsernamePasswordCredentials("username", "password");
ByteArrayOutputStream bos = new ByteArrayOutputStream();
CipherOutputStream cos = new CipherOutputStream(bos, aesCipher);
cos.write(objectToByteArray(cred));
cos.close();
String eCred = Base64.encodeBase64String(bos.toByteArray());
For all three code snippets, is the code correct? Considering that this code will be called frequently, which approach is the most efficient?

Java Android - Encrypt/Decrypt file contents

So at the moment I'm trying to write some encrypted text to a file and then be able to read that back in, decrypt it and display it to the user. I'm currently using AES-256 with PBKDF2 password derivation as I'd like to be able to use a user's password to encrypt/decrypt the files. The files are simple text files. The code I am currently using to encrypt some text and save it to a file is below. As far as I can tell, from having a look using adb, this works correctly.
FileOutputStream out = new FileOutputStream(mypath);
String defaultMessage = "Empty File";
int iterationCount = 1000;
int keyLength = 256;
int saltLength = keyLength / 8;
SecureRandom randomGenerator = new SecureRandom();
byte[] salt = new byte[saltLength];
randomGenerator.nextBytes(salt);
KeySpec keySpec = new PBEKeySpec(submittedPassword.toCharArray(), salt, iterationCount, keyLength);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
byte[] keyBytes = keyFactory.generateSecret(keySpec).getEncoded();
SecretKey key = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
byte[] iv = new byte[cipher.getBlockSize()];
randomGenerator.nextBytes(iv);
IvParameterSpec ivParams = new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, key, ivParams);
byte[] ciphertext = cipher.doFinal(defaultMessage.getBytes("UTF-8"));
String finalMessage = ciphertext.toString() + "]" + iv.toString() + "]" + salt.toString();
out.write(finalMessage.getBytes());
out.close();
P.S The above is within a Try/Except.
The code below is what I'm currently trying to use to read in the file and then decrypt it, however, when I try to display the decrypted contents via the test view at the end, it does not show up.
FileInputStream fileInputStream = new FileInputStream(mypath);
InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
StringBuffer stringBuffer = new StringBuffer();
while ((fileContents = bufferedReader.readLine()) != null) {
stringBuffer.append(fileContents + "\n");
}
String fileContentsString = stringBuffer.toString();
String[] fileContentsList = fileContentsString.split("]");
byte[] cipherText = fileContentsList[0].getBytes();
Toast.makeText(getApplicationContext(), fileContentsList[0], Toast.LENGTH_LONG).show();
byte[] iv = fileContentsList[1].getBytes();
byte[] salt = fileContentsList[2].getBytes();
KeySpec keySpec = new PBEKeySpec(submittedPassword.toCharArray(), salt, 1000, 256);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
byte[] keyBytes = keyFactory.generateSecret(keySpec).getEncoded();
SecretKey key = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivParams = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, key, ivParams);
byte[] plaintext = cipher.doFinal(cipherText);
String plainrStr = new String(plaintext , "UTF-8");
textEdit.setText(plainrStr);
Hopefully someone can provide me with some assistance here. Again, the second code segment is within a Try/Except statement.
You have multiple problems with your code.
Encryption
This code
String finalMessage = ciphertext.toString() + "]" + iv.toString() + "]" + salt.toString();
does not produce a ciphertext. See here: Java: Syntax and meaning behind "[B#1ef9157"? Binary/Address?
The IV and salt have fixed sizes, so they can be placed in front of the ciphertext. After you've written the whole ciphertext, you need to use something like Base64 or Hex in order to get a String. Modern ciphers like AES produce ciphertexts that can contain bytes of any value which don't always constitute valid character encodings such as UTF-8. Strings are no containers for arbitrary byte[] contents.
ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write(iv);
baos.write(salt);
baos.write(ciphertext);
String finalMessage = Base64.encodeToString(baos.toByteArray(), Base64.DEFAULT);
But you don't need that at all, because you can directly write your ciphertext into the file:
out.write(iv);
out.write(salt);
out.write(ciphertext);
Decryption
Don't use InputStreamReader, a BufferedReader and a StringBuffer for binary data. Otherwise, you'll corrupt your binary ciphertext.
You only need this:
byte[] iv = new byte[16];
byte[] salt = new byte[32];
byte[] ctChunk = new byte[8192]; // not for whole ciphertext, just a buffer
if (16 != fileInputStream.read(iv) || 32 != fileInputStream.read(salt)) {
throw new Exception("IV or salt too short");
}
KeySpec keySpec = new PBEKeySpec(submittedPassword.toCharArray(), salt, 1000, 256);
SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
byte[] keyBytes = keyFactory.generateSecret(keySpec).getEncoded();
SecretKey key = new SecretKeySpec(keyBytes, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
IvParameterSpec ivParams = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, key, ivParams);
int read;
ByteArrayOutputStream ctBaos = new ByteArrayOutputStream();
while((read = fileInputStream.read(ctChunk)) > 0) {
ctBaos.write(cipher.update(cipherText, 0, read));
}
ctBaos.write(cipher.doFinal());
String plainrStr = new String(ctBaos.toByteArray(), "UTF-8");
textEdit.setText(plainrStr);
This handles randomization properly but doesn't provide integrity. If you want to detect (malicious) manipulations of your ciphertexts (and generally you'll want that to prevent some attacks), you'd need to use an authenticated mode like GCM or EAX, or employ an encrypt-then-MAC scheme with a strong MAC like HMAC-SHA256.
Use a library like tozny/java-aes-crypto in order to use good defaults.
How to write some encrypted text to a file and then be able to read that back in?
public static byte[] generateKey(String password) throws Exception
{
byte[] keyStart = password.getBytes("UTF-8");
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG", "Crypto");
sr.setSeed(keyStart);
kgen.init(128, sr);
SecretKey skey = kgen.generateKey();
return skey.getEncoded();
}
public static byte[] encodeFile(byte[] key, byte[] fileData) throws Exception
{
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(fileData);
return encrypted;
}
public static byte[] decodeFile(byte[] key, byte[] fileData) throws Exception
{
SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] decrypted = cipher.doFinal(fileData);
return decrypted;
}
To save a encrypted file to sd do:
File file = new File(Environment.getExternalStorageDirectory() + File.separator + "your_folder_on_sd", "file_name");
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
byte[] yourKey = generateKey("password");
byte[] filesBytes = encodeFile(yourKey, yourByteArrayContainigDataToEncrypt);
bos.write(fileBytes);
bos.flush();
bos.close();
To decode a file use:
byte[] yourKey = generateKey("password");
byte[] decodedData = decodeFile(yourKey, bytesOfYourFile);
For reading in a file to a byte Array there a different way out there. A Example: http://examples.javacodegeeks.com/core-java/io/fileinputstream/read-file-in-byte-array-with-fileinputstream/

StreamCorruptedException during serialised object decryption (and other byte fields)

my software raised StreamCorruptedException during a deciphering:
My cipher is AES/CBC/PKCS5Padding and my key is obtained with PBKey Derivation method, so I need to create a salt to generate AES128 key.
My goal is to obtain a file formed in this way:
(I will delete exception management code to improve readability)
My cipher code:
char[] password = passwordString.toCharArray();
SecureRandom random = new SecureRandom();
byte salt[] = new byte[SALT_BYTES];
random.nextBytes(salt);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec keySpec = new PBEKeySpec(password, salt, ITERATION, AES_KEY_BITS);
SecretKey tmp = factory.generateSecret(keySpec);
SecretKey secretKey = new SecretKeySpec(tmp.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance("AES/CFB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
FileOutputStream fout = null;
ObjectOutputStream objOut = null;
fout = new FileOutputStream(PRIVATE_RING_FILENAME);
fout.write(salt);
byte[] ivN = cipher.getIV();
fout.write(ivN);
CipherOutputStream cos = new CipherOutputStream(fout, cipher);
objOut = new ObjectOutputStream(cos);
PrivateKeyRing prvKeyRing = new PrivateKeyRing();
SealedObject sealedObject = new SealedObject(prvKeyRing, cipher);
objOut.writeObject(sealedObject);
fout.close();
objOut.close();
cos.close();
and it works without problem.
My deciphering code:
char[] password = passwordString.toCharArray();
File file = new File(PRIVATE_RING_FILENAME);
FileInputStream fin = new FileInputStream(file);
Cipher cipher = Cipher.getInstance("AES/CFB/PKCS5Padding");
byte[] salt = new byte[SALT_BYTES];
fin.read(salt);
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec keySpec = new PBEKeySpec(password, salt, ITERATION, AES_KEY_BITS);
SecretKey = factory.generateSecret(keySpec);
SecretKey secretKey = new SecretKeySpec(tmp.getEncoded(), "AES");
byte[] ivN = new byte[AES_BYTES];
fin.read(ivN, 0, AES_BYTES);
cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(ivN));
CipherInputStream cis = new CipherInputStream(fin, cipher);
ObjectInputStream objIn;
PrivateKeyRing prvKeyRing = null;
SealedObject sealedObject = null;
objIn = new ObjectInputStream(cis);
sealedObject = (SealedObject) objIn.readObject();
prvKeyRing = (PrivateKeyRing) sealedObject.getObject(cipher);
objIn.close();
fin.close();
cis.close();
But StreamCorruptedException: invalid stream header: 73720019 occurs when system execute:
objIn = new ObjectInputStream(cis);
If I try to write the object without ciphering all works.
What do you think about?
I read about some problem when you try to write multiple serialised object but I think this is not the case.
This comes about because you are encrypting and decrypting twice with the same cipher. The object is first sealed with the cipher and then written to the cipher output stream, with the cipher in the state resulting from sealing the object. This does not yield a file that can be decrypted with the cipher in its initial state. You would have to unseal the object first and then read it from the stream, which is impossible. Get rid of either the cipher streams or the sealed object.

Strange padding after AES Cipher decryption(I THINK)

this is my code for Java AES encryption and decryption : the parameters are obtained using fileinputstream and secretkeyfactory.
public byte[] encrypt(byte[] plainText, SecretKey secretKey, String outputFilePath) throws Exception
{
//select putput file for encrypted text
FileOutputStream fos = new FileOutputStream(outputFilePath);
//Cipher in encrypt mode
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv));
fos.write(cipher.doFinal(plainText));
fos.close();
//Encrypted text is returned in a byte array.
return cipher.doFinal(plainText);
}
// Decryption function
public String decrypt(String sharedKeyFilepath, String cipherTextFilepath, String plainTextFilepath) throws Exception
{
FileInputStream fis = null;
File sharedKeyFile = new File(sharedKeyFilepath);
byte[] sharedKeyByte = new byte[(int)sharedKeyFile.length()];
File cipherTextFile = new File(cipherTextFilepath);
byte[] cipherText = new byte[(int)cipherTextFile.length()];
File plainTextFile = new File(plainTextFilepath);
byte[] plainText = new byte[(int)plainTextFile.length()];
fis = new FileInputStream(sharedKeyFile);
fis.read(sharedKeyByte);
fis.close();
fis = new FileInputStream(cipherTextFile);
fis.read(cipherText);
fis.close();
fis = new FileInputStream(plainTextFile);
fis.read(plainText);
fis.close();
SecretKey sharedKey = new SecretKeySpec(sharedKeyByte, 0, sharedKeyByte.length, "AES");
// Cipher in decrypt mode
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, sharedKey, new IvParameterSpec(iv));
// Select output file for decrypted cipher Text
FileOutputStream fos = new FileOutputStream("Receiver/DecryptedPlainText.txt");
String decrypted = new String(cipher.doFinal(cipherText));
byte [] decryptedBytes = cipher.doFinal(cipherText);
fos.write(decryptedBytes);
fos.close();
// Return the decrypted text as a string.
return decrypted;
}
The file i send is like :
but the decrypted version is like :
any reasons for this ? Thanks :]

getting javax.crypto.IllegalBlockSizeException error when decrypting

I'm trying to encrypt/decrypt text from a file but i am receiving the following error:
Exception in thread "main" javax.crypto.IllegalBlockSizeException: Input length must be multiple of 16 when decrypting with padded cipher
I'm using AES 128 bit with AES/ECB/PKCS5Padding. Any idea why I am getting this error?
Here is my code:
public class AES_Encryption {
public static void main(String[] args) throws Exception {
String str = new Scanner(new File("src//plainText.txt")).useDelimiter("\\Z").next();
FileWriter fstream = new FileWriter("src//cipherText.txt");
BufferedWriter out = new BufferedWriter(fstream);
FileWriter fstream2 = new FileWriter("src//decrpytedText.txt");
BufferedWriter out2 = new BufferedWriter(fstream2);
System.out.println("" + str);
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(128);
Key key = keyGen.generateKey();
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] cipherText = cipher.doFinal(str.getBytes());
String ct = new String(cipherText);
System.out.println( new String(cipherText, "UTF8") );
out.append(ct);
out.close();
String cipherT = new Scanner(new File("src//cipherText.txt")).useDelimiter("\\Z").next();
cipher.init(Cipher.DECRYPT_MODE, key);
//byte[] decVal = Base64.decode(cipherT.getBytes());
byte[] newPlainText = cipher.doFinal(cipherT.getBytes());
String dt = new String(newPlainText, "UTF8");
out2.append(dt);
out2.close();
}
}
Your error is treating the ciphertext as a string:
String ct = new String(cipherText); // <--- Noooo!
There will be values in your byte array that cannot be expressed as characters in the default charset.
Always treat your ciphertext as a byte array, even when reading or writing to files.
One get the IllegalBlockSizeException in following case as mentioned in Cipher API documentaion:
IllegalBlockSizeException - If this cipher is a block cipher, no padding has been requested (only in encryption mode), and the total input length of the data
processed by this cipher is not a multiple of block size
In Your case you are Encrypting the String correctly , But while decryption you are treating the cipherText as String and then
you are putting cipherT.getBytes() byte array in doFinal method of Cipher. The byte array conversion of String is not same as reading byte array from the file in binary mode.
The functionality and limitation of String.toBytes() as mentioned in String API documentaion is as follows:
Encodes this String into a sequence of bytes using the platform's default charset, storing the result into a new byte array. The
behavior of this method when this string cannot be encoded in the
default charset is unspecified. The CharsetEncoder class should be
used when more control over the encoding process is required.
What I suggest for you is to read the cipherText.txt File in binary mode and then put the byte array you got after reading the file in doFinal method of Cipher . I have modified your code in following way:
public class AES_Encryption {
public static void main(String[] args) throws Exception {
String str = new Scanner(new File("plainText.txt")).useDelimiter("\\t").next();
FileOutputStream fstream = new FileOutputStream("cipherText.txt");
BufferedOutputStream out = new BufferedOutputStream(fstream);
FileOutputStream fstream2 = new FileOutputStream("decrpytedText.txt");
BufferedOutputStream out2 = new BufferedOutputStream(fstream2);
System.out.println("INPUT String:\n" + str);
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(128);
Key key = keyGen.generateKey();
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] cipherText = cipher.doFinal(str.getBytes());
System.out.println("ENCRYPTED String:\n"+new String(cipherText, "UTF8") );
out.write(cipherText);
out.flush();
out.close();
//String cipherT = new Scanner(new File("cipherText.txt")).nextLine();
BufferedInputStream bfin = new BufferedInputStream(new FileInputStream(new File("cipherText.txt")));//To read the file in Binary Mode.
cipher.init(Cipher.DECRYPT_MODE, key);
int BUFFERSIZE = 1024;
byte[] readBytes = new byte[BUFFERSIZE];
byte[] data = null;
int totalRead = -1;
while( (totalRead = bfin.read(readBytes))!=-1)
{
byte[] temp = new byte[(data == null ? totalRead : data.length)];
System.arraycopy((data==null ? readBytes : data),0,temp,0, temp.length);
data = new byte[(data == null ? 0 : data.length) + totalRead];
System.arraycopy(temp, 0, data, 0, temp.length);
System.arraycopy(readBytes, 0, data, data.length - temp.length, totalRead);
}
if (data!=null)
{
byte[] newPlainText = cipher.doFinal(data);
out2.write(newPlainText);
out2.flush();
System.out.println("DECRYPTED String:\n"+new String(newPlainText,"UTF8"));
}
else
{
System.out.println("No Data Found");
}
//String dt = new String(newPlainText, "UTF8");
out2.close();
}
}
I hope this would help you in resolving the exception you getting ...

Categories