AesEncryptor.java 5.09 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
package org.jivesoftware.util;

import java.io.UnsupportedEncodingException;
import java.security.Key;
import java.security.Security;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Utility class providing symmetric AES encryption/decryption. To strengthen
 * the encrypted result, use the {@link #setKey} method to provide a custom
 * key prior to invoking the {@link #encrypt} or {@link #decrypt} methods.
 *
 * @author Tom Evans
 */
public class AesEncryptor implements Encryptor {

	private static final Logger log = LoggerFactory.getLogger(AesEncryptor.class);
	private static final String ALGORITHM = "AES/CBC/PKCS7Padding";

	private static final byte[] INIT_PARM =
	{
		(byte)0xcd, (byte)0x91, (byte)0xa7, (byte)0xc5,
		(byte)0x27, (byte)0x8b, (byte)0x39, (byte)0xe0,
		(byte)0xfa, (byte)0x72, (byte)0xd0, (byte)0x29,
		(byte)0x83, (byte)0x65, (byte)0x9d, (byte)0x74
	};

	private static final byte[] DEFAULT_KEY =
	{
		(byte)0xf2, (byte)0x46, (byte)0x5d, (byte)0x2a,
		(byte)0xd1, (byte)0x73, (byte)0x0b, (byte)0x18,
		(byte)0xcb, (byte)0x86, (byte)0x95, (byte)0xa3,
		(byte)0xb1, (byte)0xe5, (byte)0x89, (byte)0x27
	};

    private static boolean isInitialized = false;

	private byte[] cipherKey = null;

    /** Default constructor */
    public AesEncryptor() { initialize(); }

    /** Custom key constructor */
    public AesEncryptor(String key) { 
    	initialize();
    	setKey(key);
    }

	/* (non-Javadoc)
	 * @see org.jivesoftware.util.Encryptor#encrypt(java.lang.String)
	 */
	@Override
	public String encrypt(String value)
	{
		if (value == null) { return null; }
		byte [] bytes = null;
		try { bytes = value.getBytes("UTF-8"); }
		catch (UnsupportedEncodingException uee) { bytes = value.getBytes(); }
		return Base64.encodeBytes( cipher(bytes, getKey(), Cipher.ENCRYPT_MODE) );
	}

	/* (non-Javadoc)
	 * @see org.jivesoftware.util.Encryptor#decrypt(java.lang.String)
	 */
	@Override
	public String decrypt(String value)
	{
		if (value == null) { return null; }
		byte [] bytes = cipher(Base64.decode(value), getKey(), Cipher.DECRYPT_MODE);
		if (bytes == null) { return null; }
		String result = null;
		try { result = new String(bytes,"UTF-8"); }
		catch (UnsupportedEncodingException uee) { result = new String(bytes); }
		return result;
	}

	/**
	 * Symmetric encrypt/decrypt routine.
	 *
	 * @param attribute The value to be converted
	 * @param key The encryption key
	 * @param mode The cipher mode (encrypt or decrypt)
	 * @return The converted attribute, or null if conversion fails
	 */
	private byte [] cipher(byte [] attribute, byte [] key, int mode)
	{
		byte [] result = null;
		try
		{
		    // Create AES encryption key
		    Key aesKey = new SecretKeySpec(key, "AES");

		    // Create AES Cipher
		    Cipher aesCipher = Cipher.getInstance(ALGORITHM);

		    // Initialize AES Cipher and convert
		    aesCipher.init(mode, aesKey, new IvParameterSpec(INIT_PARM));
		    result = aesCipher.doFinal(attribute);
		}
		catch (Exception e)
		{
			log.error("AES cipher failed", e);
		}
		return result;
	}

	/**
	 * Return the encryption key. This will return the user-defined
	 * key (if available) or a default encryption key.
	 *
	 * @return The encryption key
	 */
	private byte [] getKey()
	{
		return cipherKey == null ? DEFAULT_KEY : cipherKey;
	}

	/**
	 * Set the encryption key. This will apply the user-defined key,
	 * truncated or filled (via the default key) as needed  to meet
	 * the key length specifications.
	 *
	 * @param key The encryption key
	 */
	private void setKey(byte [] key)
	{
		cipherKey = editKey(key);
	}

	/* (non-Javadoc)
	 * @see org.jivesoftware.util.Encryptor#setKey(java.lang.String)
	 */
	@Override
	public void setKey(String key)
	{
		if (key == null) { 
			cipherKey = null; 
			return;
		}
		byte [] bytes = null;
		try { bytes = key.getBytes("UTF-8"); }
		catch (UnsupportedEncodingException uee) { bytes = key.getBytes(); }
		setKey(editKey(bytes));
	}

	/**
	 * Validates an optional user-defined encryption key. Only the
	 * first sixteen bytes of the input array will be used for the key.
	 * It will be filled (if necessary) to a minimum length of sixteen.
	 *
	 * @param key The user-defined encryption key
	 * @return A valid encryption key, or null
	 */
	private byte [] editKey(byte [] key)
	{
		if (key == null) { return null; }
		byte [] result = new byte [DEFAULT_KEY.length];
		for (int x=0; x<DEFAULT_KEY.length; x++)
		{
			result[x] = x < key.length ? key[x] : DEFAULT_KEY[x];
		}
		return result;
	}

	/** Installs the required security provider(s) */
	private synchronized void initialize()
	{
		if (!isInitialized)
		{
			try
			{
		        Security.addProvider(new BouncyCastleProvider());
				isInitialized = true;
			}
			catch (Throwable t)
			{
				log.warn("JCE provider failure; unable to load BC", t);
			}
		}
	}

/* */
	
}