Java tutorial
package jeffaschenk.tomcat.zuul.util; /* * Copyright (C) 2011 www.itcsolutions.eu * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1, or (at your * option) any later version. * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * */ /** * * @author Catalin - www.itcsolutions.eu * @version 2011 * original package: eu.itcsolutions.bc.aes; */ import org.apache.commons.io.IOUtils; import org.apache.commons.io.output.ByteArrayOutputStream; import org.bouncycastle.jce.provider.BouncyCastleProvider; import javax.crypto.*; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.io.*; import java.security.*; import java.security.spec.AlgorithmParameterSpec; import java.util.Arrays; /** * initial Implementation of AES * Bouncy Castle API installed as a JCA provider * CBC mode for encryption and decryption * * @author Catalin Boja * @author Jeff Schenk */ public class KeyFileUtils { // The default block size public static int blockSize = 16; Cipher encryptCipher = null; Cipher decryptCipher = null; // Buffer used to transport the bytes from one stream to another byte[] buf = new byte[blockSize]; //input buffer byte[] obuf = new byte[512]; //output buffer // The key byte[] key = null; // The initialization vector needed by the CBC mode byte[] IV = null; public KeyFileUtils() { //for a 192 key you must install the unrestricted policy files // from the JCE/JDK downloads page // Key Must be divisible by 16 evenly. key = "SECRET_1SECRET_20001010204060201".getBytes(); //default IV value initialized with 0 IV = new byte[blockSize]; } public KeyFileUtils(String pass, byte[] iv) { //get the key and the IV key = pass.getBytes(); IV = new byte[blockSize]; System.arraycopy(iv, 0, IV, 0, iv.length); } public KeyFileUtils(byte[] pass, byte[] iv) { //get the key and the IV key = new byte[pass.length]; System.arraycopy(pass, 0, key, 0, pass.length); IV = new byte[blockSize]; System.arraycopy(iv, 0, IV, 0, iv.length); } public void InitCiphers() throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException { //1. create the cipher using Bouncy Castle Provider encryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding", new BouncyCastleProvider()); //2. create the key SecretKey keyValue = new SecretKeySpec(key, "AES"); //3. create the IV AlgorithmParameterSpec IVspec = new IvParameterSpec(IV); //4. init the cipher encryptCipher.init(Cipher.ENCRYPT_MODE, keyValue, IVspec); //1 create the cipher decryptCipher = Cipher.getInstance("AES/CBC/PKCS5Padding", new BouncyCastleProvider()); //2. the key is already created //3. the IV is already created //4. init the cipher decryptCipher.init(Cipher.DECRYPT_MODE, keyValue, IVspec); } public void ResetCiphers() { encryptCipher = null; decryptCipher = null; } public void CBCEncrypt(InputStream fis, OutputStream fos) throws IOException, ShortBufferException, IllegalBlockSizeException, BadPaddingException { //optionally put the IV at the beginning of the cipher file //fos.write(IV, 0, IV.length); byte[] buffer = new byte[blockSize]; int noBytes = 0; byte[] cipherBlock = new byte[encryptCipher.getOutputSize(buffer.length)]; int cipherBytes; while ((noBytes = fis.read(buffer)) != -1) { cipherBytes = encryptCipher.update(buffer, 0, noBytes, cipherBlock); fos.write(cipherBlock, 0, cipherBytes); } //always call doFinal cipherBytes = encryptCipher.doFinal(cipherBlock, 0); fos.write(cipherBlock, 0, cipherBytes); //close the files fos.close(); fis.close(); } public byte[] CBCDecrypt(InputStream fis) throws IOException, ShortBufferException, IllegalBlockSizeException, BadPaddingException { // get the IV from the file // DO NOT FORGET TO reinit the cipher with the IV //fis.read(IV,0,IV.length); //this.InitCiphers(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { byte[] buffer = new byte[blockSize]; int noBytes = 0; byte[] cipherBlock = new byte[decryptCipher.getOutputSize(buffer.length)]; int cipherBytes; while ((noBytes = fis.read(buffer)) != -1) { cipherBytes = decryptCipher.update(buffer, 0, noBytes, cipherBlock); baos.write(cipherBlock, 0, cipherBytes); } // always call doFinal cipherBytes = decryptCipher.doFinal(cipherBlock, 0); baos.write(cipherBlock, 0, cipherBytes); // return the decrypted Password / Key Phrase return baos.toByteArray(); } finally { //close the input file fis.close(); baos.close(); } } /** * Helper method to Obtain the Validated Password from the Command Line. * * @param prompt - Display Prompt * @return char[] - Character Array of Validated/Confirmed Entered Data. */ private static char[] obtainValidatedPassword(String prompt) { char[] initialPassword = readPassword(prompt); // Now prompt to confirm entered Password... char[] confirmedPassword = readPassword("Please Confirm " + prompt); if (Arrays.equals(initialPassword, confirmedPassword)) { Arrays.fill(confirmedPassword, ' '); return initialPassword; } else { Arrays.fill(initialPassword, ' '); Arrays.fill(confirmedPassword, ' '); System.out.println("Passwords do not Match, unable to continue!"); return null; } } /** * Helper method to read a Password from the command line, * hiding input if in true terminal and not IDE. * * @param prompt - Display Prompt * @return char[] - Character Array of Entered Data. */ private static char[] readPassword(String prompt) { System.out.print(prompt); if (System.console() != null) { return System.console().readPassword(); } else { try { return new BufferedReader(new InputStreamReader(System.in)).readLine().toCharArray(); } catch (IOException ioe) { return null; } } } /** * Utility Main to provide capabilities to generate a protected KeyFile for use with Zuul * * @param args - Incoming program Arguments * @throws Exception */ public static void main(String[] args) throws Exception { // Initialize our Ciphers KeyFileUtils keyFileUtils = new KeyFileUtils(); keyFileUtils.InitCiphers(); // Assume we our sole purpose in life is to prompt for a Password, which is the same, // ensure we have an argument to be out filename. if ((args == null) || (args.length != 1)) { System.out.println("Invalid number of Arguments specified!"); System.out.println( "Usage: " + keyFileUtils.getClass().getName() + " <Fully Qualified Output Key Filename>"); return; } // Validate Argument as a file. File outputKeyFile = new File(args[0]); if (outputKeyFile.exists()) { // Prompt to overwrite... char[] answer = readPassword("Specified Key File:[" + outputKeyFile + "], exists. Overwrite? (y/n)? "); if ((answer == null) || (answer.length == 0) || (!String.valueOf(answer).toUpperCase().startsWith("Y"))) { System.out.println( "Existing Key File:[" + outputKeyFile + "], exists. Will not be Overwritten, Done."); return; } else { outputKeyFile.delete(); } } // Prompt Operator for the Zuul Encryption Password to be encrypted in a file and saved for later access. // Prompt to overwrite... char[] password = obtainValidatedPassword("Enter Zuul Encryption Password: "); if (password == null) { System.out.println("Unable to obtain Password, Done."); return; } // Create our Input and Output Streams for the encryption of the Password. InputStream is = IOUtils.toInputStream(String.valueOf(password), "UTF-8"); FileOutputStream fos = new FileOutputStream(outputKeyFile); // Encrypt keyFileUtils.CBCEncrypt(is, fos); // Now lets us Validate the file. keyFileUtils.ResetCiphers(); keyFileUtils = new KeyFileUtils(); keyFileUtils.InitCiphers(); // Obtain the Decrypted Contents byte[] obtainedDecryptedData = keyFileUtils.CBCDecrypt(new FileInputStream(outputKeyFile)); // Compare to validate... boolean valid = String.valueOf(password).equals(new String(obtainedDecryptedData)); Arrays.fill(password, ' '); Arrays.fill(obtainedDecryptedData, (byte) 0x00); if (valid) { System.out.println("Key File:[" + outputKeyFile + "], validated and available for use, Done."); return; } else { System.out.println("Key File:[" + outputKeyFile + "], Did not Validate, very bad!"); return; } } // End of Main. }