org.apache.myfaces.util.StateUtils Maven / Gradle / Ivy
Show all versions of myfaces-commons Show documentation
/*
* Copyright 2004-2006 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.myfaces.util;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.faces.FacesException;
import javax.faces.context.FacesContext;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
/**
* This Class exposes a handful of methods related to encryption,
* compression and serialization.
*
*
* - ISO-8859-1 is the character set used.
* - GZIP is used for all compression/decompression.
* - Base64 is used for all encoding and decoding.
*
*
* To enable encryption, a secret must be provided. StateUtils looks first
* for the org.apache.myfaces.secret init param, then system properties.
* If a secret cannot be located, encryption is not used.
*
*
* - DES is the default encryption algorithm
* - ECB is the default mode
* - PKCS5Padding is the default padding
* - The default algorithm can be overridden using the
* org.apache.myfaces.algorithm parameter
* - The default mode and padding can be overridden using the
* org.apache.myfaces.algorithm.parameters parameter
* - The defaults are not recommended
* - This utility has not been tested with modes other than ECB and CBC
* - An initialization vector can be specified via the
* org.apache.myfaces.algorithm.parameters parameter
*
*
* All parameters are interpretted as base 64 encoded keys. In other
* words, if your secret is "76543210", you would put "NzY1NDMyMTA=" in
* the deployment descriptor. This is needed so that key values are not
* limited to just values composed of printable characters.
*
* If you are using CBC mode encryption, you must specify an
* initialization vector. StateUtils will throw an exception otherwise.
*
* If you are using the AES algorithm and getting a SecurityException
* complaining about keysize, you most likely need to get the unlimited
* strength jurisdiction policy files from a place like
* http://java.sun.com/j2se/1.4.2/download.html .
*
* @author Dennis C. Byrne, ich
*/
public class StateUtils {
private static final Log log = LogFactory.getLog(StateUtils.class);
public static final String ZIP_CHARSET = "ISO-8859-1";
// encryption related properties
private static byte[] secret;
private static String algorithm = "DES";
private static String algorithmParams = "ECB/PKCS5Padding";
private static byte[] iv; // initialization vector for CBC mode
public static final String INIT_PREFIX = "org.apache.myfaces.";
public static final String INIT_SECRET = INIT_PREFIX + "secret";
public static final String INIT_ALGORITHM = INIT_PREFIX + "algorithm";
public static final String INIT_ALGORITHM_IV = INIT_PREFIX + "algorithm.iv";
public static final String INIT_ALGORITHM_PARAM = INIT_PREFIX + "algorithm.parameters";
private static boolean secure = false;
static
{
FacesContext ctx = FacesContext.getCurrentInstance();
String _secret;
String _algorithm;
String _algorithmParams;
String _iv; // initialization vector
if (ctx != null)
{
_secret = ctx.getExternalContext().getInitParameter(INIT_SECRET);
_algorithm = ctx.getExternalContext().getInitParameter(INIT_ALGORITHM);
_algorithmParams = ctx.getExternalContext().getInitParameter(INIT_ALGORITHM_PARAM);
_iv = ctx.getExternalContext().getInitParameter(INIT_ALGORITHM_IV);
} else {
// TODO remove this
// not recommended for users ... mostly here for testing
_secret = System.getProperty(INIT_SECRET);
_algorithm = System.getProperty(INIT_ALGORITHM);
_algorithmParams = System.getProperty(INIT_ALGORITHM_PARAM);
_iv = System.getProperty(INIT_ALGORITHM_IV);
}
if (_secret == null)
{
log.debug("secret for " + INIT_SECRET + " not located, encryption disabled");
}
else
{
if (_algorithmParams != null && _algorithmParams.startsWith("CBC") & _iv == null )
{
throw new FacesException(INIT_ALGORITHM_PARAM +
" parameter has been set with CBC mode," +
" but no initialization vector has been set " +
" with " + INIT_ALGORITHM_IV);
}
secret = new Base64().decode(_secret.getBytes());
if (_algorithm != null)
algorithm = _algorithm;
if (_algorithmParams != null)
algorithmParams = _algorithmParams;
if (_iv != null)
iv = new Base64().decode(_iv.getBytes());
secure = true;
log.debug("encryption enabled using " + algorithm + "/" + algorithmParams);
}
}
public static boolean isSecure()
{
return secure;
}
/**
* This fires during the Render Response phase.
*/
public static final String construct(Object object){
byte[] bytes = getAsByteArray(object);
if(secure)
bytes = encrypt(bytes);
bytes = compress(bytes);
bytes = encode(bytes);
try
{
return new String(bytes, ZIP_CHARSET);
}
catch (UnsupportedEncodingException e)
{
throw new FacesException(e);
}
}
public static final byte[] getAsByteArray(Object object)
{
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try
{
ObjectOutputStream writer = new ObjectOutputStream(outputStream);
writer.writeObject(object);
byte[] bytes = outputStream.toByteArray();
writer.close();
outputStream.close();
writer = null;
outputStream = null;
return bytes;
}
catch (IOException e)
{
throw new FacesException(e);
}
}
public static byte[] encrypt(byte[] insecure)
{
try
{
// note: keep it local to avoid threading issues
SecretKey secretKey = new SecretKeySpec(secret, algorithm);
Cipher cipher = Cipher.getInstance(algorithm + "/" + algorithmParams);
if(iv != null){
IvParameterSpec ivSpec = new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
}else{
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
}
return cipher.doFinal(insecure);
}
catch (Exception e)
{
throw new FacesException(e);
}
}
public static final byte[] compress(byte[] bytes)
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try
{
GZIPOutputStream gzip = new GZIPOutputStream(baos);
gzip.write(bytes, 0, bytes.length);
gzip.finish();
byte[] fewerBytes = baos.toByteArray();
gzip.close();
baos.close();
gzip = null;
baos = null;
return fewerBytes;
}
catch (IOException e)
{
throw new FacesException(e);
}
}
public static final byte[] encode(byte[] bytes)
{
return new Base64().encode(bytes);
}
/**
* This fires during the Restore View phase.
*/
public static final Object reconstruct(String string){
byte[] bytes;
try
{
bytes = string.getBytes(ZIP_CHARSET);
bytes = decode(bytes);
bytes = decompress(bytes);
if(secure)
bytes = decrypt(bytes);
return getAsObject(bytes);
}
catch (UnsupportedEncodingException e)
{
throw new FacesException(e);
}
}
public static final byte[] decode(byte[] bytes)
{
return new Base64().decode(bytes);
}
public static final byte[] decompress(byte[] bytes)
{
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[2048];
int length;
try
{
GZIPInputStream gis = new GZIPInputStream(bais);
while ((length = gis.read(buffer)) != -1)
{
baos.write(buffer, 0, length);
}
byte[] moreBytes = baos.toByteArray();
baos.close();
bais.close();
gis.close();
baos = null;
bais = null;
gis = null;
return moreBytes;
}
catch (IOException e)
{
throw new FacesException(e);
}
}
public static byte[] decrypt(byte[] secure)
{
try
{
// note: keep it local to avoid threading issues
SecretKey secretKey = new SecretKeySpec(secret, algorithm);
Cipher cipher = Cipher.getInstance(algorithm + "/" + algorithmParams);
if(iv != null)
{
IvParameterSpec ivSpec = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);
}
else
{
cipher.init(Cipher.DECRYPT_MODE, secretKey);
}
return cipher.doFinal(secure);
}
catch (Exception e)
{
throw new FacesException(e);
}
}
public static final Object getAsObject(byte[] bytes)
{
ByteArrayInputStream input = new ByteArrayInputStream(bytes);
try
{
ObjectInputStream s = new ObjectInputStream(input);
Object object = s.readObject();
s.close();
input.close();
s = null;
input = null;
return object;
}
catch (Exception e)
{
throw new FacesException(e);
}
}
public static String encode64(Object obj)
{
try
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
OutputStream zos = new GZIPOutputStream(baos);
ObjectOutputStream oos = new ObjectOutputStream(zos);
oos.writeObject(obj);
oos.close();
zos.close();
baos.close();
Base64 base64Codec = new Base64();
return new String(base64Codec.encode( baos.toByteArray() ), ZIP_CHARSET);
}
catch (IOException e)
{
log.fatal("Cannot encode Object with Base64", e);
throw new FacesException(e);
}
}
public static byte[] getSecret()
{
return secret;
}
public static void setSecret(String _secret)
{
secret = new Base64().decode(_secret.getBytes());
}
public static void setSecret(byte[] secret)
{
StateUtils.secret = secret;
}
public static String getAlgorithm()
{
return algorithm;
}
public static void setAlgorithm(String algorithm)
{
StateUtils.algorithm = algorithm;
}
public static String getAlgorithmParams()
{
return algorithmParams;
}
public static void setAlgorithmParams(String algorithmParams)
{
StateUtils.algorithmParams = algorithmParams;
}
public static byte[] getIv()
{
return iv;
}
public static void setIv(String _iv)
{
iv = new Base64().decode(_iv.getBytes());
}
public static void setIv(byte[] iv)
{
StateUtils.iv = iv;
}
public static void main (String[] args) throws UnsupportedEncodingException
{
byte[] bytes = encode(args[0].getBytes(ZIP_CHARSET));
System.out.println(new String(bytes, ZIP_CHARSET));
}
}