반응형
package ase;

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.AlgorithmParameters;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.InvalidParameterSpecException;
import java.security.spec.KeySpec;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.IllegalBlockSizeException;
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;

public class AES128_File {

	public static String byteToHex(byte[] raw) {

		String HEXES = "0123456789ABCDEF";

		if (raw == null) {
			return null;
		}
		final StringBuilder hex = new StringBuilder(2 * raw.length);
		for (final byte b : raw) {
			hex.append(HEXES.charAt((b & 0xF0) >> 4)).append(HEXES.charAt((b & 0x0F)));
		}
		return hex.toString();
	}

	public static byte[] hexToByte(String hexString) {
		int len = hexString.length();

		byte[] ba = new byte[len / 2];

		for (int i = 0; i < len; i += 2) {
			ba[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) + Character.digit(hexString.charAt(i + 1), 16));
		}

		return ba;
	}

	/**
	 * @param msg
	 */
	private void cryptMessage(String msg) {
		System.out.println("** Crypt ** " + msg);
	}
	/**
	 * @param input
	 *            - the cleartext file to be encrypted
	 * @param output
	 *            - the encrypted data file
	 * @throws IOException
	 * @throws IllegalBlockSizeException
	 * @throws BadPaddingException
	 */
	public void WriteEncryptedFile(String cryptKey,InputStream inputStream, OutputStream outputStream) throws IOException, IllegalBlockSizeException, BadPaddingException {
		try {
			

			int SALT_LEN = 8;
			byte[] mInitVec = null;
			byte[] mSalt = new byte[SALT_LEN];
			Cipher mEcipher = null;
			int KEYLEN_BITS = 128; // see notes below where this is used.
			int ITERATIONS = 65536;
			int MAX_FILE_BUF = 2048;

			long totalread = 0;
			int nread = 0;
			byte[] inbuf = new byte[MAX_FILE_BUF];
			SecretKeyFactory factory = null;
			SecretKey tmp = null;

			mSalt = new byte[SALT_LEN];
			SecureRandom rnd = new SecureRandom();
			
			rnd.nextBytes(mSalt);
			cryptMessage("generated salt :" + byteToHex(mSalt));
			factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");

			KeySpec spec = new PBEKeySpec(cryptKey.toCharArray(), mSalt, ITERATIONS, KEYLEN_BITS);

			tmp = factory.generateSecret(spec);

			SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");

			mEcipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
			mEcipher.init(Cipher.ENCRYPT_MODE, secret);

			AlgorithmParameters params = mEcipher.getParameters();

			mInitVec = params.getParameterSpec(IvParameterSpec.class).getIV();

			System.out.println("mInitVec : " + mInitVec);
			cryptMessage("mInitVec is :" + byteToHex(mInitVec));
			outputStream.write(mSalt);
			outputStream.write(mInitVec);

			while ((nread = inputStream.read(inbuf)) > 0) {
				cryptMessage("read " + nread + " bytes");
				totalread += nread;

				byte[] trimbuf = new byte[nread];

				for (int i = 0; i < nread; i++) {
					trimbuf[i] = inbuf[i];
				}

				byte[] tmpBuf = mEcipher.update(trimbuf);

				if (tmpBuf != null) {
					outputStream.write(tmpBuf);
				}
			}

			byte[] finalbuf = mEcipher.doFinal();

			if (finalbuf != null) {
				outputStream.write(finalbuf);
			}

			outputStream.flush();
			inputStream.close();
			outputStream.close();
			outputStream.close();
			cryptMessage("wrote " + totalread + " encrypted bytes");
			System.out.println("파일 암호화 성공");
		} catch (InvalidKeyException ex) {
			Logger.getLogger(AES128_File.class.getName()).log(Level.SEVERE, null, ex);
		} catch (InvalidParameterSpecException ex) {
			Logger.getLogger(AES128_File.class.getName()).log(Level.SEVERE, null, ex);
		} catch (NoSuchAlgorithmException ex) {
			Logger.getLogger(AES128_File.class.getName()).log(Level.SEVERE, null, ex);
		} catch (NoSuchPaddingException ex) {
			Logger.getLogger(AES128_File.class.getName()).log(Level.SEVERE, null, ex);
		} catch (InvalidKeySpecException ex) {
			Logger.getLogger(AES128_File.class.getName()).log(Level.SEVERE, null, ex);
		}
		
	}

	/**
	 * Read from the encrypted file (input) and turn the cipher back into
	 * cleartext. Write the cleartext buffer back out to disk as (output) File.
	 * 
	 * I left CipherInputStream in here as a test to see if I could mix it with
	 * the update() and final() methods of encrypting and still have a correctly
	 * decrypted file in the end. Seems to work so left it in.
	 * 
	 * @param input
	 *            - File object representing encrypted data on disk
	 * @param output
	 *            - File object of cleartext data to write out after decrypting
	 * @throws IllegalBlockSizeException
	 * @throws BadPaddingException
	 * @throws IOException
	 */
	public void ReadEncryptedFile(String cryptKey,InputStream inputStream, OutputStream outputStream) throws IllegalBlockSizeException, BadPaddingException, IOException {
		try {

			int SALT_LEN = 8;
			byte[] mInitVec = null;
			byte[] mSalt = new byte[SALT_LEN];
			Cipher mDecipher = null;
			int KEYLEN_BITS = 128; // see notes below where this is used.
			int ITERATIONS = 65536;
			int MAX_FILE_BUF = 2048;

			CipherInputStream cin;
			long totalread = 0;
			int nread = 0;
			byte[] inbuf = new byte[MAX_FILE_BUF];

			SecretKeyFactory factory = null;
			SecretKey tmp = null;
			// SecretKey secret = null;
			

			// Read the Salt
			inputStream.read(mSalt);
			cryptMessage("generated salt :" + byteToHex(mSalt));

			factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");

			KeySpec spec = new PBEKeySpec(cryptKey.toCharArray(), mSalt, ITERATIONS, KEYLEN_BITS);

			tmp = factory.generateSecret(spec);

			SecretKey secret = new SecretKeySpec(tmp.getEncoded(), "AES");

			mDecipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
			mDecipher.init(Cipher.ENCRYPT_MODE, secret);

			AlgorithmParameters params = mDecipher.getParameters();
			mInitVec = params.getParameterSpec(IvParameterSpec.class).getIV();

			inputStream.read(mInitVec);
			cryptMessage("mInitVec is :" + byteToHex(mInitVec));
			mDecipher.init(Cipher.DECRYPT_MODE, secret, new IvParameterSpec(mInitVec));

			cin = new CipherInputStream(inputStream, mDecipher);

			System.out.println("inbuf.length : " + inbuf.length);

			while ((nread = cin.read(inbuf)) > 0) {
				// Db("read " + nread + " bytes");
				totalread += nread;

				byte[] trimbuf = new byte[nread];

				for (int i = 0; i < nread; i++) {
					trimbuf[i] = inbuf[i];
				}

				// write out the size-adjusted buffer
				outputStream.write(trimbuf);
			}

			outputStream.flush();
			cin.close();
			inputStream.close();
			outputStream.close();
			cryptMessage("wrote " + totalread + " encrypted bytes");
		} catch (Exception ex) {
			ex.getStackTrace();
			Logger.getLogger(AES128_File.class.getName()).log(Level.SEVERE, null, ex);
		}
	}

	/**
	 * adding main() for usage demonstration. With member vars, some of the
	 * locals would not be needed
	 */
	public static void main(String[] args) {

		// create the input.txt file in the current directory before continuing
		File input = new File("C:\\Test\\test.txt");
		File eoutput = new File("C:\\Test\\test.aes");
		File doutput = new File("C:\\Test\\decrypted1.txt");
		String iv = null;
		String salt = null;
		AES128_File aesFile = new AES128_File();
		
		String cryptKey = "1q2w3e";

		/*
		 * write out encrypted file
		 */
		try {
			aesFile.WriteEncryptedFile(cryptKey,new FileInputStream(input), new FileOutputStream(eoutput));
			System.out.printf("File encrypted to " + eoutput.getName() + "\niv:" + iv + "\nsalt:" + salt + "\n\n");
		} catch (IllegalBlockSizeException | BadPaddingException | IOException e) {
			e.printStackTrace();
		}

		/*
		 * decrypt file
		 */
		/*
		 * write out decrypted file
		 */
		try {
			aesFile.ReadEncryptedFile(cryptKey,new FileInputStream(eoutput), new FileOutputStream(doutput));
			System.out.println("decryption finished to " + doutput.getName());
		} catch (IllegalBlockSizeException | BadPaddingException | IOException e) {
			e.printStackTrace();
		}
	}
}


+ Recent posts