AES encryption of files (and strings) in java with randomization of IV (initialization vector)
http://siberean.livejournal.com/14788.html
Java encryption-decryption examples, I've seen so far in Internet, are having IV been hard coded, i.e. not changed every time. However randomization of the initialization vector (IV) is a must for AES and for strong security (WEP was compromised because of hardcoding of IV). Notice that IV is not a "salt", and is not a secret, but like a cryptographic nonce - must be randomized each time.
In simple example below - IV is attached in the beginning of the stream.
import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.File; import java.io.InputStream; import java.io.OutputStream; import java.security.SecureRandom; import java.util.Arrays; import javax.crypto.Cipher; import javax.crypto.CipherInputStream; import javax.crypto.CipherOutputStream; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; public class Encryption { private static final int IV_LENGTH=16; /* A helper - to reuse the stream code below - if a small String is to be encrypted */ public static byte[] encrypt(String plainText, String password) throws Exception { ByteArrayInputStream bis = new ByteArrayInputStream(plainText.getBytes("UTF8")); ByteArrayOutputStream bos = new ByteArrayOutputStream(); encrypt(bis, bos, password); return bos.toByteArray(); } public static byte[] decrypt(String cipherText, String password) throws Exception { byte[] cipherTextBytes = cipherText.getBytes(); ByteArrayInputStream bis = new ByteArrayInputStream(cipherTextBytes); ByteArrayOutputStream bos = new ByteArrayOutputStream(); decrypt(bis, bos, password); return bos.toByteArray(); } public static void encrypt(InputStream in, OutputStream out, String password) throws Exception{ SecureRandom r = new SecureRandom(); byte[] iv = new byte[IV_LENGTH]; r.nextBytes(iv); out.write(iv); //write IV as a prefix out.flush(); //System.out.println(">>>>>>>>written"+Arrays.toString(iv)); Cipher cipher = Cipher.getInstance("AES/CFB8/NoPadding"); //"DES/ECB/PKCS5Padding";"AES/CBC/PKCS5Padding" SecretKeySpec keySpec = new SecretKeySpec(password.getBytes(), "AES"); IvParameterSpec ivSpec = new IvParameterSpec(iv); cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec); out = new CipherOutputStream(out, cipher); byte[] buf = new byte[1024]; int numRead = 0; while ((numRead = in.read(buf)) >= 0) { out.write(buf, 0, numRead); } out.close(); } public static void decrypt(InputStream in, OutputStream out, String password) throws Exception{ byte[] iv = new byte[IV_LENGTH]; in.read(iv); //System.out.println(">>>>>>>>red"+Arrays.toString(iv)); Cipher cipher = Cipher.getInstance("AES/CFB8/NoPadding"); //"DES/ECB/PKCS5Padding";"AES/CBC/PKCS5Padding" SecretKeySpec keySpec = new SecretKeySpec(password.getBytes(), "AES"); IvParameterSpec ivSpec = new IvParameterSpec(iv); cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); in = new CipherInputStream(in, cipher); byte[] buf = new byte[1024]; int numRead = 0; while ((numRead = in.read(buf)) >= 0) { out.write(buf, 0, numRead); } out.close(); } public static void copy(int mode, String inputFile, String outputFile, String password) throws Exception { BufferedInputStream is = new BufferedInputStream(new FileInputStream(inputFile)); BufferedOutputStream os = new BufferedOutputStream(new FileOutputStream(outputFile)); if(mode==Cipher.ENCRYPT_MODE){ encrypt(is, os, password); } else if(mode==Cipher.DECRYPT_MODE){ decrypt(is, os, password); } else throw new Exception("unknown mode"); is.close(); os.close(); } public static void main(String[] args){ if(args.length<1){ System.out.println("Pass at least one argument (filename)"); return; } try{ //check files - just for safety String fileName=args[0]; String tempFileName=fileName+".enc"; String resultFileName=fileName+".dec"; File file = new File(fileName); if(!file.exists()){ System.out.println("No file "+fileName); return; } File file2 = new File(tempFileName); File file3 = new File(resultFileName); if(file2.exists() || file3.exists()){ System.out.println("File for encrypted temp file or for the result decrypted file already exists. Please remove it or use a different file name"); return; } copy(Cipher.ENCRYPT_MODE, fileName, tempFileName, "password12345678"); copy(Cipher.DECRYPT_MODE, tempFileName, resultFileName, "password12345678"); System.out.println("Success. Find encrypted and decripted files in current directory"); } catch(Exception e){ e.printStackTrace(); } } }
Usage:
$ javac Encryption.java
Pass any existing file, you want to encrypt through command line argument (test.sh in the following example):
$ java Encryption test.sh
Success. Find encrypted and decripted files in current directory
Encrypted file (test.enc):
$ cat test.sh.enc
&X▒b▒▒▒▒_▒▒$Z▒▒f▒XboM▒ ▒_f§R▒s▒♣▒▒K▒M;▒▒▒▒'L▒ZS◄;▒▒i
▒▒|VØ▒:?▒▒▒?▒9y{7"▒▒▒▒+▒▒e}▒▒yi▒▒y_/jjU:▒▒_▒ ►p▒?▒▒▒;\[lE▒▒▒▒Cpc▒46▒▒▒▒@▒<▒n▒↓I▒
▒▒s▒?b▒p▒O▒▒▒▒▒▒▒\d▒4n3'▒▒▒Y♦<▒▒▒▒▒▒>▒▒▒▒Ih▒▒▒´\▒↓_R▒vGW▒▒▒V▒▒?(Q♥G J◄DMS▒▒▒
zC;*
Let's check the decrypted file (test.dec):
$ cat test.sh.dec #!/bin/sh i=0 depth=6 nodes_number=$(echo "2^$depth" | bc) #echo "total nodes: $nodes_number" while [ $i -lt $nodes_number ] ;do number=$(echo "obase=2;$i" | bc) printf "%0${depth}o\n" 0$number i=`expr $i + 1` done
The file is readable.