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.

posted on 2017-06-02 17:27  寻步  阅读(271)  评论(0编辑  收藏  举报