All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.myfaces.shared_tomahawk.util.StateUtils Maven / Gradle / Ivy

There is a newer version: 4.2.9
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.shared_tomahawk.util;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.UnsupportedEncodingException;
import java.security.AccessController;
import java.security.NoSuchAlgorithmException;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Arrays;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.faces.FacesException;
import javax.faces.application.ViewExpiredException;
import javax.faces.context.ExternalContext;
import javax.servlet.ServletContext;

import org.apache.commons.codec.binary.Base64;
import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;
import org.apache.myfaces.shared_tomahawk.util.serial.SerialFactory;

/**
 * 

This Class exposes a handful of methods related to encryption, * compression and serialization of the view state.

* *
    *
  • ISO-8859-1 is the character set used.
  • *
  • GZIP is used for all compression/decompression.
  • *
  • Base64 is used for all encoding and decoding.
  • *
  • DES is the default encryption algorithm
  • *
  • ECB is the default mode
  • *
  • PKCS5Padding is the default padding
  • *
  • HmacSHA1 is the default MAC algorithm
  • *
  • 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
  • *
  • This class has not been tested with modes other than ECB and CBC
  • *
  • An initialization vector can be specified via the * org.apache.myfaces.ALGORITHM.IV parameter
  • *
  • The default MAC algorithm can be overridden using the * org.apache.myfaces.MAC_ALGORITHM parameter
  • *
* *

The secret is interpretted as base 64 encoded. 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.

* *

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 * @see org.apache.myfaces.webapp.StartupServletContextListener */ public final class StateUtils { //private static final Log log = LogFactory.getLog(StateUtils.class); private static final Logger log = Logger.getLogger(StateUtils.class.getName()); public static final String ZIP_CHARSET = "ISO-8859-1"; public static final String DEFAULT_ALGORITHM = "DES"; public static final String DEFAULT_ALGORITHM_PARAMS = "ECB/PKCS5Padding"; public static final String INIT_PREFIX = "org.apache.myfaces."; /** * Indicate if the view state is encrypted or not. By default, encryption is enabled. */ @JSFWebConfigParam(name="org.apache.myfaces.USE_ENCRYPTION",since="1.1",defaultValue="true",expectedValues="true,false",group="state") public static final String USE_ENCRYPTION = INIT_PREFIX + "USE_ENCRYPTION"; /** * Defines the secret (Base64 encoded) used to initialize the secret key * for encryption algorithm. See MyFaces wiki/web site documentation * for instructions on how to configure an application for * different encryption strengths. */ @JSFWebConfigParam(name="org.apache.myfaces.SECRET",since="1.1",group="state") public static final String INIT_SECRET = INIT_PREFIX + "SECRET"; /** * Indicate the encryption algorithm used for encrypt the view state. */ @JSFWebConfigParam(name="org.apache.myfaces.ALGORITHM",since="1.1",defaultValue="DES",group="state",tags="performance") public static final String INIT_ALGORITHM = INIT_PREFIX + "ALGORITHM"; /** * If is set to "false", the secret key used for encryption algorithm is not cached. This is used * when the returned SecretKey for encryption algorithm is not thread safe. */ @JSFWebConfigParam(name="org.apache.myfaces.SECRET.CACHE",since="1.1",group="state") public static final String INIT_SECRET_KEY_CACHE = INIT_SECRET + ".CACHE"; /** * Defines the initialization vector (Base64 encoded) used for the encryption algorithm */ @JSFWebConfigParam(name="org.apache.myfaces.ALGORITHM.IV",since="1.1",group="state") public static final String INIT_ALGORITHM_IV = INIT_ALGORITHM + ".IV"; /** * Defines the default mode and padding used for the encryption algorithm */ @JSFWebConfigParam(name="org.apache.myfaces.ALGORITHM.PARAMETERS",since="1.1",defaultValue="ECB/PKCS5Padding",group="state") public static final String INIT_ALGORITHM_PARAM = INIT_ALGORITHM + ".PARAMETERS"; /** * Defines the factory class name using for serialize/deserialize the view state returned * by state manager into a byte array. The expected class must implement * org.apache.myfaces.shared_tomahawk.util.serial.SerialFactory interface. */ @JSFWebConfigParam(name="org.apache.myfaces.SERIAL_FACTORY", since="1.1",group="state",tags="performance") public static final String SERIAL_FACTORY = INIT_PREFIX + "SERIAL_FACTORY"; /** * Indicate if the view state should be compressed before encrypted(optional) and encoded */ @JSFWebConfigParam(name="org.apache.myfaces.COMPRESS_STATE_IN_CLIENT",since="1.1",defaultValue="false",expectedValues="true,false",group="state",tags="performance") public static final String COMPRESS_STATE_IN_CLIENT = INIT_PREFIX + "COMPRESS_STATE_IN_CLIENT"; public static final String DEFAULT_MAC_ALGORITHM = "HmacSHA1"; /** * Indicate the algorithm used to calculate the Message Authentication Code that is * added to the view state. */ @JSFWebConfigParam(name="org.apache.myfaces.MAC_ALGORITHM",defaultValue="HmacSHA1",group="state",tags="performance") public static final String INIT_MAC_ALGORITHM = "org.apache.myfaces.MAC_ALGORITHM"; /** * Define the initialization code that are used to initialize the secret key used * on the Message Authentication Code algorithm */ @JSFWebConfigParam(name="org.apache.myfaces.MAC_SECRET",group="state") public static final String INIT_MAC_SECRET = "org.apache.myfaces.MAC_SECRET"; /** * If is set to "false", the secret key used for MAC algorithm is not cached. This is used * when the returned SecretKey for mac algorithm is not thread safe. */ @JSFWebConfigParam(name="org.apache.myfaces.MAC_SECRET.CACHE",group="state") public static final String INIT_MAC_SECRET_KEY_CACHE = "org.apache.myfaces.MAC_SECRET.CACHE"; /** Utility class, do not instatiate */ private StateUtils() { //nope } private static void testConfiguration(ExternalContext ctx){ String algorithmParams = ctx.getInitParameter(INIT_ALGORITHM_PARAM); if (algorithmParams == null) { algorithmParams = ctx.getInitParameter(INIT_ALGORITHM_PARAM.toLowerCase()); } String iv = ctx.getInitParameter(INIT_ALGORITHM_IV); if (iv == null) { iv = ctx.getInitParameter(INIT_ALGORITHM_IV.toLowerCase()); } if (algorithmParams != null && algorithmParams.startsWith("CBC") ) { if(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); } } public static boolean enableCompression(ExternalContext ctx) { if(ctx == null) throw new NullPointerException("ExternalContext ctx"); return "true".equals(ctx.getInitParameter(COMPRESS_STATE_IN_CLIENT)); } public static boolean isSecure(ExternalContext ctx) { if(ctx == null) throw new NullPointerException("ExternalContext ctx"); return ! "false".equals(ctx.getInitParameter(USE_ENCRYPTION)); } /** * This fires during the Render Response phase, saving state. */ public static final String construct(Object object, ExternalContext ctx){ byte[] bytes = getAsByteArray(object, ctx); if( enableCompression(ctx) ) bytes = compress(bytes); if(isSecure(ctx)) bytes = encrypt(bytes, ctx); bytes = encode(bytes); try { return new String(bytes, ZIP_CHARSET); } catch (UnsupportedEncodingException e) { throw new FacesException(e); } } /** * Performs serialization with the serialization provider created by the * SerialFactory. * * @param object * @param ctx * @return */ public static final byte[] getAsByteArray(Object object, ExternalContext ctx) { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); // get the Factory that was instantiated @ startup SerialFactory serialFactory = (SerialFactory) ctx.getApplicationMap().get(SERIAL_FACTORY); if(serialFactory == null) throw new NullPointerException("serialFactory"); try { ObjectOutputStream writer = serialFactory.getObjectOutputStream(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, ExternalContext ctx) { if (ctx == null) throw new NullPointerException("ExternalContext ctx"); testConfiguration(ctx); SecretKey secretKey = (SecretKey) getSecret(ctx); String algorithm = findAlgorithm(ctx); String algorithmParams = findAlgorithmParams(ctx); byte[] iv = findInitializationVector(ctx); SecretKey macSecretKey = (SecretKey) getMacSecret(ctx); String macAlgorithm = findMacAlgorithm(ctx); try { // keep local to avoid threading issue Mac mac = Mac.getInstance(macAlgorithm); mac.init(macSecretKey); 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); } if (log.isLoggable(Level.FINE)) { log.fine("encrypting w/ " + algorithm + "/" + algorithmParams); } //EtM Composition Approach int macLenght = mac.getMacLength(); byte[] secure = new byte[cipher.getOutputSize(insecure.length)+ macLenght]; int secureCount = cipher.doFinal(insecure,0,insecure.length,secure); mac.update(secure, 0, secureCount); mac.doFinal(secure, secureCount); return secure; } 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, restoring state. */ public static final Object reconstruct(String string, ExternalContext ctx) { byte[] bytes; try { if(log.isLoggable(Level.FINE)) log.fine("Processing state : "+string); bytes = string.getBytes(ZIP_CHARSET); bytes = decode(bytes); if(isSecure(ctx)) bytes = decrypt(bytes, ctx); if( enableCompression(ctx) ) bytes = decompress(bytes); return getAsObject(bytes, ctx); } catch (Throwable e) { if (log.isLoggable(Level.FINE)) { log.log(Level.FINE, "View State cannot be reconstructed", e); } return null; } } public static final byte[] decode(byte[] bytes) { return new Base64().decode(bytes); } public static final byte[] decompress(byte[] bytes) { if(bytes == null) throw new NullPointerException("byte[] bytes"); ByteArrayInputStream bais = new ByteArrayInputStream(bytes); ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer = new byte[bytes.length]; 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, ExternalContext ctx) { if (ctx == null) throw new NullPointerException("ExternalContext ctx"); testConfiguration(ctx); SecretKey secretKey = (SecretKey) getSecret(ctx); String algorithm = findAlgorithm(ctx); String algorithmParams = findAlgorithmParams(ctx); byte[] iv = findInitializationVector(ctx); SecretKey macSecretKey = (SecretKey) getMacSecret(ctx); String macAlgorithm = findMacAlgorithm(ctx); try { // keep local to avoid threading issue Mac mac = Mac.getInstance(macAlgorithm); mac.init(macSecretKey); 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); } if (log.isLoggable(Level.FINE)) { log.fine("decrypting w/ " + algorithm + "/" + algorithmParams); } //EtM Composition Approach int macLenght = mac.getMacLength(); mac.update(secure, 0, secure.length-macLenght); byte[] signedDigestHash = mac.doFinal(); boolean isMacEqual = true; for (int i = 0; i < signedDigestHash.length; i++) { if (signedDigestHash[i] != secure[secure.length-macLenght+i]) { isMacEqual = false; // MYFACES-2934 Must compare *ALL* bytes of the hash, // otherwise a side-channel timing attack is theorically possible // but with a very very low probability, because the // comparison time is too small to be measured compared to // the overall request time and in real life applications, // there are too many uncertainties involved. //break; } } if (!isMacEqual) { throw new ViewExpiredException(); } return cipher.doFinal(secure, 0, secure.length-macLenght); } catch (Exception e) { throw new FacesException(e); } } /** * Performs deserialization with the serialization provider created from the * SerialFactory. * * @param bytes * @param ctx * @return */ public static final Object getAsObject(byte[] bytes, ExternalContext ctx) { ByteArrayInputStream input = null; try { input = new ByteArrayInputStream(bytes); // get the Factory that was instantiated @ startup SerialFactory serialFactory = (SerialFactory) ctx.getApplicationMap().get(SERIAL_FACTORY); if(serialFactory == null) throw new NullPointerException("serialFactory"); ObjectInputStream s = null; Exception pendingException = null; try { s = serialFactory.getObjectInputStream(input); Object object = null; if (System.getSecurityManager() != null) { final ObjectInputStream ois = s; object = AccessController.doPrivileged(new PrivilegedExceptionAction() { //Put IOException and ClassNotFoundException as "checked" exceptions, //so AccessController wrap them in a PrivilegedActionException public Object run() throws PrivilegedActionException, IOException, ClassNotFoundException { return ois.readObject(); } }); // Since s has the same instance as ois, // we don't need to close it here, rather // close it on the finally block related to s // and avoid duplicate close exceptions // finally // { // ois.close(); // } } else { object = s.readObject(); } return object; } catch (Exception e) { pendingException = e; throw new FacesException(e); } finally { if (s != null) { try { s.close(); } catch (IOException e) { // If a previous exception is thrown // ignore this, but if not, wrap it in a // FacesException and throw it. In this way // we preserve the original semantic of this // method, but we handle correctly the case // when we close a stream. Obviously, the // information about this exception is lost, // but note that the interesting information // is always on pendingException, since we // only do a readObject() on the outer try block. if (pendingException == null) { throw new FacesException(e); } } finally { s = null; } } } } finally { if (input != null) { try { input.close(); } catch (IOException e) { //ignore it, because ByteArrayInputStream.close has //no effect, but it is better to call close and preserve //semantic from previous code. } finally { input = null; } } } } /** * Utility method for generating base 64 encoded strings. * * @param args * @throws UnsupportedEncodingException */ public static void main (String[] args) throws UnsupportedEncodingException { byte[] bytes = encode(args[0].getBytes(ZIP_CHARSET)); System.out.println(new String(bytes, ZIP_CHARSET)); } private static byte[] findInitializationVector(ExternalContext ctx) { byte[] iv = null; String _iv = ctx.getInitParameter(INIT_ALGORITHM_IV); if(_iv == null) { _iv = ctx.getInitParameter(INIT_ALGORITHM_IV.toLowerCase()); } if (_iv != null) iv = new Base64().decode(_iv.getBytes()); return iv; } private static String findAlgorithmParams(ExternalContext ctx) { String algorithmParams = ctx.getInitParameter(INIT_ALGORITHM_PARAM); if (algorithmParams == null) { algorithmParams = ctx.getInitParameter(INIT_ALGORITHM_PARAM.toLowerCase()); } if (algorithmParams == null) { algorithmParams = DEFAULT_ALGORITHM_PARAMS; } if (log.isLoggable(Level.FINE)) { log.fine("Using algorithm paramaters " + algorithmParams); } return algorithmParams; } private static String findAlgorithm(ExternalContext ctx) { String algorithm = ctx.getInitParameter(INIT_ALGORITHM); if (algorithm == null) { algorithm = ctx.getInitParameter(INIT_ALGORITHM.toLowerCase()); } return findAlgorithm( algorithm ); } private static String findAlgorithm(ServletContext ctx) { String algorithm = ctx.getInitParameter(INIT_ALGORITHM); if (algorithm == null) { algorithm = ctx.getInitParameter(INIT_ALGORITHM.toLowerCase()); } return findAlgorithm( algorithm ); } private static String findAlgorithm(String initParam) { if (initParam == null) { initParam = DEFAULT_ALGORITHM; } if (log.isLoggable(Level.FINE)) { log.fine("Using algorithm " + initParam); } return initParam; } /** * Does nothing if the user has disabled the SecretKey cache. This is * useful when dealing with a JCA provider whose SecretKey * implementation is not thread safe. * * Instantiates a SecretKey instance based upon what the user has * specified in the deployment descriptor. The SecretKey is then * stored in application scope where it can be used for all requests. */ public static void initSecret(ServletContext ctx){ if(ctx == null) throw new NullPointerException("ServletContext ctx"); if (log.isLoggable(Level.FINE)) log.fine("Storing SecretKey @ " + INIT_SECRET_KEY_CACHE); // Create and store SecretKey on application scope String cache = ctx.getInitParameter(INIT_SECRET_KEY_CACHE); if(cache == null) { cache = ctx.getInitParameter(INIT_SECRET_KEY_CACHE.toLowerCase()); } if (!"false".equals(cache)) { String algorithm = findAlgorithm(ctx); // you want to create this as few times as possible ctx.setAttribute(INIT_SECRET_KEY_CACHE, new SecretKeySpec(findSecret(ctx, algorithm), algorithm)); } if (log.isLoggable(Level.FINE)) log.fine("Storing SecretKey @ " + INIT_MAC_SECRET_KEY_CACHE); String macCache = ctx.getInitParameter(INIT_MAC_SECRET_KEY_CACHE); if(macCache == null) { macCache = ctx.getInitParameter(INIT_MAC_SECRET_KEY_CACHE.toLowerCase()); } if (!"false".equals(macCache)) { String macAlgorithm = findMacAlgorithm(ctx); // init mac secret and algorithm ctx.setAttribute(INIT_MAC_SECRET_KEY_CACHE, new SecretKeySpec(findMacSecret(ctx, macAlgorithm), macAlgorithm)); } } private static SecretKey getSecret(ExternalContext ctx) { Object secretKey = (SecretKey) ctx.getApplicationMap().get(INIT_SECRET_KEY_CACHE); if (secretKey == null) { String cache = ctx.getInitParameter(INIT_SECRET_KEY_CACHE); if(cache == null) { cache = ctx.getInitParameter(INIT_SECRET_KEY_CACHE.toLowerCase()); } if ("false".equals(cache)) { // No cache is used. This option is activated String secret = ctx.getInitParameter(INIT_SECRET); if (secret == null) { secret = ctx.getInitParameter(INIT_SECRET.toLowerCase()); } if (secret == null) { throw new NullPointerException("Could not find secret using key '" + INIT_SECRET + "'"); } String algorithm = findAlgorithm(ctx); secretKey = new SecretKeySpec(findSecret(ctx, algorithm), algorithm); } else { throw new NullPointerException("Could not find SecretKey in application scope using key '" + INIT_SECRET_KEY_CACHE + "'"); } } if( ! ( secretKey instanceof SecretKey ) ) throw new ClassCastException("Did not find an instance of SecretKey " + "in application scope using the key '" + INIT_SECRET_KEY_CACHE + "'"); return (SecretKey) secretKey; } private static byte[] findSecret(ExternalContext ctx, String algorithm) { String secret = ctx.getInitParameter(INIT_SECRET); if (secret == null) { secret = ctx.getInitParameter(INIT_SECRET.toLowerCase()); } return findSecret(secret, algorithm); } private static byte[] findSecret(ServletContext ctx, String algorithm) { String secret = ctx.getInitParameter(INIT_SECRET); if (secret == null) { secret = ctx.getInitParameter(INIT_SECRET.toLowerCase()); } return findSecret(secret, algorithm); } private static byte[] findSecret(String secret, String algorithm) { byte[] bytes = null; if(secret == null) { try { KeyGenerator kg = KeyGenerator.getInstance(algorithm); bytes = kg.generateKey().getEncoded(); if(log.isLoggable(Level.FINE)) log.fine("generated random password of length " + bytes.length); } catch (NoSuchAlgorithmException e) { // Generate random password length 8, int length = 8; bytes = new byte[length]; new Random().nextBytes(bytes); if(log.isLoggable(Level.FINE)) log.fine("generated random password of length " + length); } } else { bytes = new Base64().decode(secret.getBytes()); } return bytes; } private static String findMacAlgorithm(ExternalContext ctx) { String algorithm = ctx.getInitParameter(INIT_MAC_ALGORITHM); if (algorithm == null) { algorithm = ctx.getInitParameter(INIT_MAC_ALGORITHM.toLowerCase()); } return findMacAlgorithm( algorithm ); } private static String findMacAlgorithm(ServletContext ctx) { String algorithm = ctx.getInitParameter(INIT_MAC_ALGORITHM); if (algorithm == null) { algorithm = ctx.getInitParameter(INIT_MAC_ALGORITHM.toLowerCase()); } return findMacAlgorithm( algorithm ); } private static String findMacAlgorithm(String initParam) { if (initParam == null) { initParam = DEFAULT_MAC_ALGORITHM; } if (log.isLoggable(Level.FINE)) { log.fine("Using algorithm " + initParam); } return initParam; } private static SecretKey getMacSecret(ExternalContext ctx) { Object secretKey = (SecretKey) ctx.getApplicationMap().get(INIT_MAC_SECRET_KEY_CACHE); if (secretKey == null) { String cache = ctx.getInitParameter(INIT_MAC_SECRET_KEY_CACHE); if(cache == null) { cache = ctx.getInitParameter(INIT_MAC_SECRET_KEY_CACHE.toLowerCase()); } if ("false".equals(cache)) { // No cache is used. This option is activated String secret = ctx.getInitParameter(INIT_MAC_SECRET); if (secret == null) { secret = ctx.getInitParameter(INIT_MAC_SECRET.toLowerCase()); } if (secret == null) { throw new NullPointerException("Could not find secret using key '" + INIT_MAC_SECRET + "'"); } String macAlgorithm = findMacAlgorithm(ctx); secretKey = new SecretKeySpec(findMacSecret(ctx, macAlgorithm), macAlgorithm); } else { throw new NullPointerException("Could not find SecretKey in application scope using key '" + INIT_MAC_SECRET_KEY_CACHE + "'"); } } if( ! ( secretKey instanceof SecretKey ) ) throw new ClassCastException("Did not find an instance of SecretKey " + "in application scope using the key '" + INIT_MAC_SECRET_KEY_CACHE + "'"); return (SecretKey) secretKey; } private static byte[] findMacSecret(ExternalContext ctx, String algorithm) { String secret = ctx.getInitParameter(INIT_MAC_SECRET); if (secret == null) { secret = ctx.getInitParameter(INIT_MAC_SECRET.toLowerCase()); } return findMacSecret(secret, algorithm); } private static byte[] findMacSecret(ServletContext ctx, String algorithm) { String secret = ctx.getInitParameter(INIT_MAC_SECRET); if (secret == null) { secret = ctx.getInitParameter(INIT_MAC_SECRET.toLowerCase()); } return findMacSecret(secret, algorithm); } private static byte[] findMacSecret(String secret, String algorithm) { byte[] bytes = null; if(secret == null) { try { KeyGenerator kg = KeyGenerator.getInstance(algorithm); bytes = kg.generateKey().getEncoded(); if(log.isLoggable(Level.FINE)) log.fine("generated random mac password of length " + bytes.length); } catch (NoSuchAlgorithmException e) { // Generate random password length 8, int length = 8; bytes = new byte[length]; new Random().nextBytes(bytes); if(log.isLoggable(Level.FINE)) log.fine("generated random mac password of length " + length); } } else { bytes = new Base64().decode(secret.getBytes()); } return bytes; } }