test.PBEncryptLink.java Source code

Java tutorial

Introduction

Here is the source code for test.PBEncryptLink.java

Source

/*
* Copyright (c) 2008 Princeton University
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE EXPECTED TO
* PROVIDE ANY MANNER OF SUPPORT OR ASSISTANCE IN THE USE, MANAGEMENT,
* MAINTENANCE, MODIFICATION, INSTALLATION OR REPAIR OF THE SOFTWARE
* OR ANY OTHER ACTIVITIES ARISING FROM, OUT OF OR IN CONNECTION WITH
* THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

/**
 * Collection of methods to encrypt and decrypt data attached to a URL.
 */
package test;

import java.io.FileInputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Calendar;
import java.util.Properties;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;
import org.apache.log4j.Logger;

/**
 * @author tdtrue
 * 
 */
public class PBEncryptLink {

    /**
     * @author tdtrue
     * 
     */
    public enum CipherMode {
        /**
         * 
         */
        ENCRYPT,
        /**
         * 
         */
        DECRYPT,
    }

    /**
     * @param args
     * @throws InvalidAlgorithmParameterException
     * @throws UnsupportedEncodingException
     *             Shouldn't happen.
     */
    public static void main(String[] args) throws InvalidAlgorithmParameterException, UnsupportedEncodingException {
        String keyString = "Password";
        String iv = null;
        if (args.length > 0) {
            keyString = args[0];
        }

        if (args.length > 1) {
            iv = args[1];
        }

        String testEncryptedLink = null;
        String testDecryptedLink = null;
        String urlEncoded = null;
        String urlDecoded = null;
        String encodedIV = null;
        String decodedIV = null;
        byte[] encryptKeyBytes = null;
        byte[] decryptKeyBytes = null;

        try {
            /*
             * Instantiate two PBEncryptLink objects
             */
            PBEncryptLink encryptLink = new PBEncryptLink();
            PBEncryptLink decryptLink = new PBEncryptLink();

            /*
             * Convert password string to byte array; one copy for each action.
             */
            encryptKeyBytes = encryptLink.stringTo_iso8859_1_Bytes(keyString);
            decryptKeyBytes = encryptKeyBytes.clone();

            /*
             * Set object mode.
             */
            encryptLink.setCipherMode(CipherMode.ENCRYPT);
            decryptLink.setCipherMode(CipherMode.DECRYPT);

            /*
             * Set Initialization vector if one was passed.
             */
            if (iv != null) {
                encryptLink.setIV(iv);
            }

            /*
             * Initialize encrypt object and zero password bytes.
             */
            encryptLink.initCipher(encryptKeyBytes);
            zeroBytes(encryptKeyBytes);

            /*
             * Do the same for the decrypt object and also print out the IV.
             */
            decryptLink.setIV(encryptLink.getIV());
            encodedIV = new String(encryptLink.getIV(), "ISO8859_1");
            decodedIV = new String(Base64.encodeBase64(encryptLink.getIV()));
            decryptLink.initCipher(decryptKeyBytes);
            zeroBytes(decryptKeyBytes);

            /*
             * Encrypt the link and decrypt it.
             */
            String testCourse = "GEN101";
            String testUser = "ci_test";
            Logger testLogger = null;
            testEncryptedLink = encryptLink.encryptContextString(testCourse, testUser, testLogger);
            urlEncoded = URLEncoder.encode(testEncryptedLink, "UTF-8");
            urlDecoded = URLDecoder.decode(urlEncoded, "UTF-8");
            testDecryptedLink = decryptLink.decryptString(urlDecoded);
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        }

        /*
         * Display results.
         */
        System.out.println("Results:");
        System.out.println("Unencoded IV: " + decodedIV);
        System.out.println("Encoded IV: " + encodedIV);
        System.out.println("Encrypted data: " + testEncryptedLink);
        System.out.println("Encrypted data url encoded: " + urlEncoded);
        System.out.println("encrypted data decoded: " + urlDecoded);
        System.out.println("Decrypted data: " + testDecryptedLink);

        System.exit(0);

    }

    /**
     * Zero out an array of bytes.
     * 
     * @param encryptKeyBytes
     */
    public static void zeroBytes(byte[] keyBytes) {
        for (int i = 0; i < keyBytes.length; i++) {
            keyBytes[i] = 0;
        }
    }

    private CipherMode cipherMode_ = null;

    private Cipher blowfishCipher_ = null;

    private IvParameterSpec ivParmSpec_ = null;

    private static Properties prop = null;

    /**
     * Zero-Argument Constructor.
     * 
     * @throws NoSuchPaddingException
     *             Bad padding specified in Cipher.getInstance.
     * @throws NoSuchAlgorithmException
     *             Bad Cipher name in Cipher.getInstance.
     */
    public PBEncryptLink() throws NoSuchAlgorithmException, NoSuchPaddingException {

        InputStream in = null;

        try {
            in = new FileInputStream(
                    "/usr/local/xythos/wfs/webapps/xythosticketagent/WEB-INF/src/blowfish.properties");
            prop = new Properties();
            prop.load(in);
        } catch (Exception ex) {
            // ignore
        }

        blowfishCipher_ = Cipher.getInstance("blowfish/cbc/PKCS5Padding");
    }

    public static String getPropValue(String inKey) {
        if (prop == null)
            return "";
        return prop.getProperty(inKey);
    }

    /**
     * Decrypt a Base64 string of ISO8859_1 data
     * 
     * @param encryptedString
     * @return decrypted string
     * @throws IllegalBlockSizeException
     *             Not clear if Blowfish can throw this.
     * @throws BadPaddingException
     *             Not clear if or when this can be thrown.
     */
    public String decryptString(String encryptedString) throws IllegalBlockSizeException, BadPaddingException {
        Cipher cipher = getBlowfishCipher();
        byte[] encryptedBytesBase64 = stringTo_iso8859_1_Bytes(encryptedString);
        byte[] encryptedBytes = Base64.decodeBase64(encryptedBytesBase64);
        byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
        String decryptedString = iso8859_1_BytesToString(decryptedBytes);
        return decryptedString;
    }

    /**
     * Given a course and user value, insert them in the context string, add the
     * current time and encrypt using the currently initialized blowfish Cipher
     * object. Note that the String is converted to an arry of bytes and treated
     * as ISO8859_1 -- reasonable in our environment. The result is returned as
     * a Base64 String.
     * 
     * @param courseName
     * @param user_id
     * @return String in Base64 representing the encrypted data.
     * @throws IllegalBlockSizeException
     *             Is this possible in Blowfish?
     * @throws BadPaddingException
     *             Shouldn't see this since default is used.
     */
    public String encryptContextString(String courseName, String user_id)
            throws IllegalBlockSizeException, BadPaddingException {
        return encryptContextString(courseName, user_id, null);
    }

    /**
     * Given a user value, insert it in the context string, add the
     * current time and encrypt using the currently initialized blowfish Cipher
     * object. Note that the String is converted to an arry of bytes and treated
     * as ISO8859_1 -- reasonable in our environment. The result is returned as
     * a Base64 String.
     * 
     * @param user_id
     * @param log
     * @return String in Base64 representing the encrypted data.
     * @throws IllegalBlockSizeException
     *             Is this possible in Blowfish?
     * @throws BadPaddingException
     *             Shouldn't see this since default is used.
     */
    public String encryptContextString(String user_id, Logger log)
            throws IllegalBlockSizeException, BadPaddingException {
        return encryptContextString(null, user_id, log);
    }

    /**
     * Given a course and user value, insert them in the context string, add the
     * current time and encrypt using the currently initialized blowfish Cipher
     * object. Note that the String is converted to an arry of bytes and treated
     * as ISO8859_1 -- reasonable in our environment. The result is returned as
     * a Base64 String.
     * 
     * @param courseName
     * @param user_id
     * @param log
     * @return String in Base64 representing the encrypted data.
     * @throws IllegalBlockSizeException
     *             Is this possible in Blowfish?
     * @throws BadPaddingException
     *             Shouldn't see this since default is used.
     */

    public String encryptContextString(String courseName, String user_id, Logger log)
            throws IllegalBlockSizeException, BadPaddingException {
        Long curTime = new Long(Calendar.getInstance().getTimeInMillis());
        long rndTime = Math.round(curTime.doubleValue() / 60000);

        if (log != null) {
            log.info("Default date and time " + (Calendar.getInstance()).toString());
        }

        Cipher cipher = getBlowfishCipher();
        String value = "login=" + user_id + (courseName != null ? ("&course=" + courseName) : "") + "&time="
                + rndTime;
        if (log != null) {
            log.info("string before encrypting=" + value.toLowerCase());
        }
        byte[] bArray = stringTo_iso8859_1_Bytes(value.toLowerCase());

        byte[] encryptedData = cipher.doFinal(bArray);
        byte[] bBase64Array = Base64.encodeBase64(encryptedData);
        String base64String = iso8859_1_BytesToString(bBase64Array);
        return base64String;
    }

    /**
     * @return the blowfishCipher
     */
    public Cipher getBlowfishCipher() {
        return blowfishCipher_;
    }

    /**
     * @return the cipherMode
     */
    public CipherMode getCipherMode() {
        return (cipherMode_ != null ? cipherMode_ : CipherMode.ENCRYPT);
    }

    /**
     * Retrieve the Initialization Vector from the stored IvParameterSpec.
     * 
     * @return the iV
     */
    public byte[] getIV() {
        IvParameterSpec ivs = getIvParmSpec();
        byte[] iv = null;
        if (ivs == null) {
            iv = new byte[0];
        } else {
            iv = ivs.getIV();
        }
        return iv;
    }

    /**
     * Retrieve the current IvParameterSpec for this object.
     * 
     * @return the ivParmSpec_
     */
    public IvParameterSpec getIvParmSpec() {
        return ivParmSpec_;
    }

    /**
     * Initialize the Cipher object using the given key. If the Initialization
     * vaector has not already been stored (as an IvParameterSpec), then, for
     * encryption, one is created and stored. If the CipherMode is DECRYPT and
     * no initialization vector has been set, then an
     * InvalidAlgorithmParameterException is thrown.
     * 
     * @param key
     * @throws InvalidKeyException
     * @throws InvalidAlgorithmParameterException
     *             Hmm.
     */
    public void initCipher(byte[] key) throws InvalidKeyException, InvalidAlgorithmParameterException {
        SecretKeySpec skeySpec = new SecretKeySpec(key, "Blowfish");
        Cipher cipher = getBlowfishCipher();
        IvParameterSpec ivs = getIvParmSpec();

        int intCipherMode = -1;
        switch (getCipherMode()) {
        case ENCRYPT:
            intCipherMode = Cipher.ENCRYPT_MODE;
            if (ivs == null) {

                cipher.init(intCipherMode, skeySpec);
                setIV(cipher.getIV());
            } else {
                cipher.init(intCipherMode, skeySpec, ivs);
            }
            break;
        case DECRYPT:
            intCipherMode = Cipher.DECRYPT_MODE;
            cipher.init(intCipherMode, skeySpec, ivs);
        default:
            break;
        }
        if (intCipherMode == -1) {
            return;
        }

    }

    /**
     * Given an array of bytes, treat them as an array of ISO8859_1 characters
     * and convert them to a Java String.
     * 
     * @param input
     *            array of bytes to convert.
     * @return String representing the array of bytes
     */
    public String iso8859_1_BytesToString(byte[] input) {
        Charset charSet = Charset.forName("ISO-8859-1");
        ByteBuffer bb = ByteBuffer.wrap(input);
        CharBuffer cb = charSet.decode(bb);
        String output = cb.toString();
        return output;
    }

    /**
     * Store a Blowfish cipher object.
     * 
     * @param blowfishCipher
     *            the blowfishCipher to set
     */
    public void setBlowfishCipher(Cipher blowfishCipher) {
        blowfishCipher_ = blowfishCipher;
    }

    /**
     * Determine whether the object will be used for encryption or decryption.
     * 
     * @param cipherMode
     *            the cipherMode to set
     */
    public void setCipherMode(CipherMode cipherMode) {
        cipherMode_ = cipherMode;
    }

    /**
     * Store the contents of a byte array as the IvParameterSpec. The array is
     * copied and can be safely modified after the IV has been set.
     * 
     * @param iv
     */
    public void setIV(byte[] iv) {
        if (iv.length != 8) {
            throw new IllegalArgumentException("The byte array supplied to setIV must be exactly 8 "
                    + "elements long. The supplied array was " + iv.length + " elements.");
        }
        IvParameterSpec ivs = new IvParameterSpec(iv);
        ivParmSpec_ = ivs;
    }

    /**
     * Use a string as an Initialization vector; the string is assumed to
     * contain only ISO8859_1 characters.
     * 
     * @param iv
     */
    public void setIV(String strIv) {
        if (strIv.length() != 8) {
            throw new IllegalArgumentException("String supplied to setIV must be exactly 8 characters long. "
                    + "The supplied string was " + strIv.length() + " characters.");
        }
        byte[] iv = stringTo_iso8859_1_Bytes(strIv);
        setIV(iv);
    }

    /**
     * Given a String containing only characters that can be represented in
     * ISO8859_1, return an array of bytes encoded in ISO8859_1 that reprsents
     * the content of the string.
     * 
     * @param input
     *            String of ISO8859_1 characters
     * @return array of bytes representing the String in ISO8859_1
     */
    public byte[] stringTo_iso8859_1_Bytes(String input) {
        Charset charSet = Charset.forName("ISO-8859-1");
        ByteBuffer bb = charSet.encode(input);
        byte[] output = new byte[bb.limit()];
        bb.get(output);
        return output;
    }

}