/* * Copyright (c) 2001 Matthew Feldt. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided the copyright notice above is * retained. * * THIS SOFTWARE IS PROVIDED ''AS IS'' AND WITHOUT ANY EXPRESSED OR * IMPLIED WARRANTIES. */ /** * TripleDES.java * * Java Examples In A Nutshell Copyright (c) 2000 David Flanagan * Exercise 6-3: * The TripleDES class of Example 6-5 uses the DESede algorithm in the default * ECB (electronic code book) mode. This encryption mode is more vulnerable * to certain decryption attacks than CBC (cipher block chaining) mode. Modify * the example so that it uses CBC mode. You specify the mode as part of the * algorithm name: in this case, instead of specifying "DESede" as the algorithm, * specify "DESede/CBC/PKCS5Padding". * * To encrypt using CBC mode, the Cipher object creates an initialization vector * (IV) of random bytes, which is also required when decrypting. Modify the * encrypt() method so that it obtains the IV with the getIV() method of the * Cipher object and writes the bytes (and the length) of the IV array to the out- * put stream before it writes out the encrypted bytes. To do this, you may want * to modify encrypt() so that it doesn't use the CipherOutputStream but * instead works with the Cipher class directly, the way decrypt does. Modify * the decrypt() method so that it reads the bytes of the IV and uses them to * create a javax.crypto.spec.IvParameterSpec object, which it then passes (as * an AlgorithmParameterSpec) to one of the init() methods of the Cipher * object. * * @author Matthew Feldt * @version 1.0, 04/16/2001 15:49 */ package com.feldt.examples.security; import javax.crypto.*; import javax.crypto.spec.*; import java.security.*; import java.security.spec.*; import java.io.*; public class TripleDES { final static String CIPHER_TRANS = "DESede/CBC/PKCS5Padding"; final static String KEY_GEN_TRANS = "DESede"; final static String USAGE = "Usage: java TripleDES -d|-e|-g "; final static int BUFSIZE = 2048; /** create TripleDES encryption/decryption key */ public static SecretKey generateKey() throws NoSuchAlgorithmException { KeyGenerator keygen = KeyGenerator.getInstance("DESede"); return keygen.generateKey(); } /** read TripleDES SecretKey from the specified file */ public static SecretKey readKey(File f) throws InvalidKeyException, InvalidKeySpecException, IOException, NoSuchAlgorithmException { DataInputStream in = new DataInputStream(new FileInputStream(f)); byte[] rawkey = new byte[(int)f.length()]; in.readFully(rawkey); in.close(); DESedeKeySpec keyspec = new DESedeKeySpec(rawkey); SecretKeyFactory keyfactory = SecretKeyFactory.getInstance("DESede"); SecretKey key = keyfactory.generateSecret(keyspec); return key; } /** write TripleDES SecretKey to the specified file */ public static void writeKey(SecretKey key, File f) throws InvalidKeySpecException, IOException, NoSuchAlgorithmException { SecretKeyFactory keyfactory = SecretKeyFactory.getInstance(KEY_GEN_TRANS); DESedeKeySpec keyspec = (DESedeKeySpec)keyfactory.getKeySpec(key, DESedeKeySpec.class); byte[] rawkey = keyspec.getKey(); // write the raw key to the file FileOutputStream out = new FileOutputStream(f); out.write(rawkey); out.close(); } /** encrypt data from InputStream to OutputStream using the specified key */ public static void encrypt(SecretKey key, InputStream in, OutputStream out) throws BadPaddingException, IllegalBlockSizeException, InvalidKeyException, IOException, NoSuchAlgorithmException, NoSuchPaddingException { Cipher cipher = Cipher.getInstance(CIPHER_TRANS); cipher.init(Cipher.ENCRYPT_MODE, key); DataOutputStream outstream = new DataOutputStream(out); byte[] iv = cipher.getIV(); // get the initialization vector outstream.writeInt(iv.length); // write IV length outstream.write(iv); // write IV // Read from the input and write to the encrypting output stream byte[] buffer = new byte[BUFSIZE]; int bytesRead; while((bytesRead = in.read(buffer)) != -1) { outstream.write(cipher.update(buffer, 0, bytesRead)); } outstream.write(cipher.doFinal()); outstream.close(); java.util.Arrays.fill(buffer, (byte)0); // clear memory } /** decrypt data from InputStream to OutputStream using the specified key */ public static void decrypt(SecretKey key, InputStream in, OutputStream out) throws BadPaddingException, IllegalBlockSizeException, InvalidAlgorithmParameterException, InvalidKeyException, IOException, NoSuchAlgorithmException, NoSuchPaddingException { Cipher cipher = Cipher.getInstance(CIPHER_TRANS); DataInputStream instream = new DataInputStream(in); int len = instream.readInt(); byte[] iv = new byte[len]; in.read(iv); IvParameterSpec ivp = new IvParameterSpec(iv); cipher.init(Cipher.DECRYPT_MODE, key, ivp); byte[] buffer = new byte[BUFSIZE]; int bytesRead; while((bytesRead = in.read(buffer)) != -1) { out.write(cipher.update(buffer, 0, bytesRead)); } out.write(cipher.doFinal()); out.flush(); } public static void main(String[] args) { try { // Check to see whether there is a provider that can do TripleDES // encryption. If not, explicitly install the SunJCE provider. try { Cipher c = Cipher.getInstance(CIPHER_TRANS); } catch(Exception e) { // An exception here probably means the JCE provider hasn't // been permanently installed on this system by listing it // in the $JAVA_HOME/jre/lib/security/java.security file. // Therefore, we have to install the JCE provider explicitly. System.err.println("Installing SunJCE provider."); Provider sunjce = new com.sun.crypto.provider.SunJCE(); Security.addProvider(sunjce); } File keyfile = new File(args[1]); // parse command line arguments if (args[0].equals("-g")) { // generate key System.out.print("Generating SecretKey, please wait... "); System.out.flush(); SecretKey key = generateKey(); writeKey(key, keyfile); System.out.println("complete."); } else if (args[0].equals("-e")) { // encrypt stdin -> stdout SecretKey key = readKey(keyfile); encrypt(key, System.in, System.out); } else if (args[0].equals("-d")) { // decrypt stdin -> stdout SecretKey key = readKey(keyfile); decrypt(key, System.in, System.out); } } catch(Exception e) { System.err.println(e); System.err.println(USAGE); System.exit(-1); } } }