Coverage Report - org.apache.myfaces.shared.util.StateUtils
 
Classes in this File Line Coverage Branch Coverage Complexity
StateUtils
72%
232/320
56%
83/146
4.515
StateUtils$1
0%
0/2
N/A
4.515
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one
 3  
  * or more contributor license agreements.  See the NOTICE file
 4  
  * distributed with this work for additional information
 5  
  * regarding copyright ownership.  The ASF licenses this file
 6  
  * to you under the Apache License, Version 2.0 (the
 7  
  * "License"); you may not use this file except in compliance
 8  
  * with the License.  You may obtain a copy of the License at
 9  
  *
 10  
  *   http://www.apache.org/licenses/LICENSE-2.0
 11  
  *
 12  
  * Unless required by applicable law or agreed to in writing,
 13  
  * software distributed under the License is distributed on an
 14  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 15  
  * KIND, either express or implied.  See the License for the
 16  
  * specific language governing permissions and limitations
 17  
  * under the License.
 18  
  */
 19  
 package org.apache.myfaces.shared.util;
 20  
 
 21  
 import java.io.ByteArrayInputStream;
 22  
 import java.io.ByteArrayOutputStream;
 23  
 import java.io.IOException;
 24  
 import java.io.ObjectInputStream;
 25  
 import java.io.ObjectOutputStream;
 26  
 import java.io.UnsupportedEncodingException;
 27  
 import java.security.AccessController;
 28  
 import java.security.NoSuchAlgorithmException;
 29  
 import java.security.PrivilegedActionException;
 30  
 import java.security.PrivilegedExceptionAction;
 31  
 import java.util.Random;
 32  
 import java.util.logging.Level;
 33  
 import java.util.logging.Logger;
 34  
 import java.util.zip.GZIPInputStream;
 35  
 import java.util.zip.GZIPOutputStream;
 36  
 
 37  
 import javax.crypto.Cipher;
 38  
 import javax.crypto.KeyGenerator;
 39  
 import javax.crypto.Mac;
 40  
 import javax.crypto.SecretKey;
 41  
 import javax.crypto.spec.IvParameterSpec;
 42  
 import javax.crypto.spec.SecretKeySpec;
 43  
 import javax.faces.FacesException;
 44  
 import javax.faces.application.ViewExpiredException;
 45  
 import javax.faces.context.ExternalContext;
 46  
 import javax.servlet.ServletContext;
 47  
 
 48  
 import org.apache.commons.codec.binary.Base64;
 49  
 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;
 50  
 import org.apache.myfaces.shared.util.serial.SerialFactory;
 51  
 
 52  
 /**
 53  
  * <p>This Class exposes a handful of methods related to encryption,
 54  
  * compression and serialization of the view state.</p>
 55  
  * 
 56  
  * <ul>
 57  
  * <li>ISO-8859-1 is the character set used.</li>
 58  
  * <li>GZIP is used for all compression/decompression.</li>
 59  
  * <li>Base64 is used for all encoding and decoding.</li>
 60  
  * <li>DES is the default encryption algorithm</li>
 61  
  * <li>ECB is the default mode</li>
 62  
  * <li>PKCS5Padding is the default padding</li>
 63  
  * <li>HmacSHA1 is the default MAC algorithm</li>
 64  
  * <li>The default algorithm can be overridden using the
 65  
  * <i>org.apache.myfaces.ALGORITHM</i> parameter</li>
 66  
  * <li>The default mode and padding can be overridden using the
 67  
  * <i>org.apache.myfaces.ALGORITHM.PARAMETERS</i> parameter</li>
 68  
  * <li>This class has not been tested with modes other than ECB and CBC</li>
 69  
  * <li>An initialization vector can be specified via the
 70  
  * <i>org.apache.myfaces.ALGORITHM.IV</i> parameter</li>
 71  
  * <li>The default MAC algorithm can be overridden using the
 72  
  * <i>org.apache.myfaces.MAC_ALGORITHM</i> parameter</li>
 73  
  * </ul>
 74  
  *
 75  
  * <p>The secret is interpretted as base 64 encoded.  In other
 76  
  * words, if your secret is "76543210", you would put "NzY1NDMyMTA=" in
 77  
  * the deployment descriptor.  This is needed so that key values are not
 78  
  * limited to just values composed of printable characters.</p>
 79  
  *
 80  
  * <p>If you are using CBC mode encryption, you <b>must</b> specify an
 81  
  * initialization vector.</p>
 82  
  *
 83  
  * <p>If you are using the AES algorithm and getting a SecurityException
 84  
  * complaining about keysize, you most likely need to get the unlimited
 85  
  * strength jurisdiction policy files from a place like
 86  
  * http://java.sun.com/j2se/1.4.2/download.html .</p>
 87  
  *
 88  
  * @see org.apache.myfaces.webapp.StartupServletContextListener
 89  
  */
 90  
 public final class StateUtils
 91  
 {
 92  
 
 93  
     //private static final Log log = LogFactory.getLog(StateUtils.class);
 94  1
     private static final Logger log = Logger.getLogger(StateUtils.class.getName());
 95  
 
 96  
     public static final String ZIP_CHARSET = "ISO-8859-1";
 97  
 
 98  
     public static final String DEFAULT_ALGORITHM = "DES";
 99  
     public static final String DEFAULT_ALGORITHM_PARAMS = "ECB/PKCS5Padding";
 100  
 
 101  
     public static final String INIT_PREFIX = "org.apache.myfaces.";
 102  
     
 103  
     /**
 104  
      * Indicate if the view state is encrypted or not. By default, encryption is enabled.
 105  
      */
 106  
     @JSFWebConfigParam(name="org.apache.myfaces.USE_ENCRYPTION",since="1.1",
 107  
             defaultValue="true",expectedValues="true,false",group="state")
 108  
     public static final String USE_ENCRYPTION = INIT_PREFIX + "USE_ENCRYPTION";
 109  
     
 110  
     /**
 111  
      * Defines the secret (Base64 encoded) used to initialize the secret key
 112  
      * for encryption algorithm. See MyFaces wiki/web site documentation 
 113  
      * for instructions on how to configure an application for 
 114  
      * different encryption strengths.
 115  
      */
 116  
     @JSFWebConfigParam(name="org.apache.myfaces.SECRET",since="1.1",group="state")
 117  
     public static final String INIT_SECRET = INIT_PREFIX + "SECRET";
 118  
     
 119  
     /**
 120  
      * Indicate the encryption algorithm used for encrypt the view state.
 121  
      */
 122  
     @JSFWebConfigParam(name="org.apache.myfaces.ALGORITHM",since="1.1",
 123  
             defaultValue="DES",group="state",tags="performance")
 124  
     public static final String INIT_ALGORITHM = INIT_PREFIX + "ALGORITHM";
 125  
 
 126  
     /**
 127  
      * If is set to "false", the secret key used for encryption algorithm is not cached. This is used
 128  
      * when the returned SecretKey for encryption algorithm is not thread safe. 
 129  
      */
 130  
     @JSFWebConfigParam(name="org.apache.myfaces.SECRET.CACHE",since="1.1",group="state")
 131  
     public static final String INIT_SECRET_KEY_CACHE = INIT_SECRET + ".CACHE";
 132  
     
 133  
     /**
 134  
      * Defines the initialization vector (Base64 encoded) used for the encryption algorithm
 135  
      */
 136  
     @JSFWebConfigParam(name="org.apache.myfaces.ALGORITHM.IV",since="1.1",group="state")
 137  
     public static final String INIT_ALGORITHM_IV = INIT_ALGORITHM + ".IV";
 138  
     
 139  
     /**
 140  
      * Defines the default mode and padding used for the encryption algorithm
 141  
      */
 142  
     @JSFWebConfigParam(name="org.apache.myfaces.ALGORITHM.PARAMETERS",since="1.1",
 143  
             defaultValue="ECB/PKCS5Padding",group="state")
 144  
     public static final String INIT_ALGORITHM_PARAM = INIT_ALGORITHM + ".PARAMETERS";
 145  
     
 146  
     /**
 147  
      * Defines the factory class name using for serialize/deserialize the view state returned 
 148  
      * by state manager into a byte array. The expected class must implement
 149  
      * org.apache.myfaces.shared.util.serial.SerialFactory interface.
 150  
      */
 151  
     @JSFWebConfigParam(name="org.apache.myfaces.SERIAL_FACTORY", since="1.1",group="state",tags="performance")
 152  
     public static final String SERIAL_FACTORY = INIT_PREFIX + "SERIAL_FACTORY";
 153  
     
 154  
     /**
 155  
      * Indicate if the view state should be compressed before encrypted(optional) and encoded
 156  
      */
 157  
     @JSFWebConfigParam(name="org.apache.myfaces.COMPRESS_STATE_IN_CLIENT",since="1.1",defaultValue="false",
 158  
             expectedValues="true,false",group="state",tags="performance")
 159  
     public static final String COMPRESS_STATE_IN_CLIENT = INIT_PREFIX + "COMPRESS_STATE_IN_CLIENT";
 160  
 
 161  
     public static final String DEFAULT_MAC_ALGORITHM = "HmacSHA1";
 162  
 
 163  
     /**
 164  
      * Indicate the algorithm used to calculate the Message Authentication Code that is
 165  
      * added to the view state.
 166  
      */
 167  
     @JSFWebConfigParam(name="org.apache.myfaces.MAC_ALGORITHM",defaultValue="HmacSHA1",
 168  
             group="state",tags="performance")
 169  
     public static final String INIT_MAC_ALGORITHM = "org.apache.myfaces.MAC_ALGORITHM";
 170  
     
 171  
     /**
 172  
      * Define the initialization code that are used to initialize the secret key used
 173  
      * on the Message Authentication Code algorithm
 174  
      */
 175  
     @JSFWebConfigParam(name="org.apache.myfaces.MAC_SECRET",group="state")
 176  
     public static final String INIT_MAC_SECRET = "org.apache.myfaces.MAC_SECRET";
 177  
 
 178  
     /**
 179  
      * If is set to "false", the secret key used for MAC algorithm is not cached. This is used
 180  
      * when the returned SecretKey for mac algorithm is not thread safe. 
 181  
      */
 182  
     @JSFWebConfigParam(name="org.apache.myfaces.MAC_SECRET.CACHE",group="state")
 183  
     public static final String INIT_MAC_SECRET_KEY_CACHE = "org.apache.myfaces.MAC_SECRET.CACHE";
 184  
     
 185  
     /** Utility class, do not instatiate */
 186  
     private StateUtils()
 187  0
     {
 188  
         //nope
 189  0
     }
 190  
 
 191  
     private static void testConfiguration(ExternalContext ctx)
 192  
     {
 193  
 
 194  102
         String algorithmParams = ctx.getInitParameter(INIT_ALGORITHM_PARAM);
 195  
         
 196  102
         if (algorithmParams == null)
 197  
         {
 198  4
             algorithmParams = ctx.getInitParameter(INIT_ALGORITHM_PARAM.toLowerCase());
 199  
         }
 200  102
         String iv = ctx.getInitParameter(INIT_ALGORITHM_IV);
 201  
         
 202  102
         if (iv == null)
 203  
         {
 204  78
             iv = ctx.getInitParameter(INIT_ALGORITHM_IV.toLowerCase());
 205  
         }
 206  
         
 207  102
         if (algorithmParams != null && algorithmParams.startsWith("CBC") )
 208  
         {
 209  26
             if(iv == null)
 210  
             {
 211  2
                 throw new FacesException(INIT_ALGORITHM_PARAM +
 212  
                         " parameter has been set with CBC mode," +
 213  
                         " but no initialization vector has been set " +
 214  
                         " with " + INIT_ALGORITHM_IV);
 215  
             }
 216  
         }
 217  
 
 218  100
     }
 219  
     
 220  
     public static boolean enableCompression(ExternalContext ctx)
 221  
     {
 222  40
         if(ctx == null)
 223  
         {
 224  0
             throw new NullPointerException("ExternalContext ctx");
 225  
         }
 226  
     
 227  40
         return "true".equals(ctx.getInitParameter(COMPRESS_STATE_IN_CLIENT));
 228  
     }
 229  
     
 230  
     public static boolean isSecure(ExternalContext ctx)
 231  
     {
 232  
         
 233  48
         if(ctx == null)
 234  
         {
 235  0
             throw new NullPointerException("ExternalContext ctx");
 236  
         }
 237  
         
 238  48
         return ! "false".equals(ctx.getInitParameter(USE_ENCRYPTION));
 239  
     }
 240  
 
 241  
     /**
 242  
      * This fires during the Render Response phase, saving state.
 243  
      */
 244  
 
 245  
     public static final String construct(Object object, ExternalContext ctx)
 246  
     {
 247  24
         byte[] bytes = getAsByteArray(object, ctx);
 248  24
         if( enableCompression(ctx) )
 249  
         {
 250  0
             bytes = compress(bytes);
 251  
         }
 252  24
         if(isSecure(ctx))
 253  
         {
 254  24
             bytes = encrypt(bytes, ctx);
 255  
         }
 256  24
         bytes = encode(bytes);
 257  
         try
 258  
         {
 259  24
             return new String(bytes, ZIP_CHARSET);
 260  
         }
 261  0
         catch (UnsupportedEncodingException e)
 262  
         {
 263  0
             throw new FacesException(e);
 264  
         }
 265  
     }
 266  
 
 267  
     /**
 268  
      * Performs serialization with the serialization provider created by the 
 269  
      * SerialFactory.  
 270  
      * 
 271  
      * @param object
 272  
      * @param ctx
 273  
      * @return
 274  
      */
 275  
     
 276  
     public static final byte[] getAsByteArray(Object object, ExternalContext ctx)
 277  
     {
 278  40
         ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
 279  
         
 280  
         // get the Factory that was instantiated @ startup
 281  40
         SerialFactory serialFactory = (SerialFactory) ctx.getApplicationMap().get(SERIAL_FACTORY);
 282  
         
 283  40
         if(serialFactory == null)
 284  
         {
 285  0
             throw new NullPointerException("serialFactory");
 286  
         }
 287  
         
 288  
         try
 289  
         {
 290  40
             ObjectOutputStream writer = serialFactory.getObjectOutputStream(outputStream);
 291  40
             writer.writeObject(object);
 292  40
             byte[] bytes = outputStream.toByteArray();
 293  40
             writer.close();
 294  40
             outputStream.close();
 295  40
             writer = null;
 296  40
             outputStream = null;
 297  40
             return bytes;
 298  
         }
 299  0
         catch (IOException e)
 300  
         {
 301  0
             throw new FacesException(e);
 302  
         }
 303  
     }
 304  
 
 305  
     public static byte[] encrypt(byte[] insecure, ExternalContext ctx)
 306  
     {
 307  
 
 308  51
         if (ctx == null)
 309  
         {
 310  0
             throw new NullPointerException("ExternalContext ctx");
 311  
         }
 312  
 
 313  51
         testConfiguration(ctx);
 314  
         
 315  50
         SecretKey secretKey = (SecretKey) getSecret(ctx);
 316  48
         String algorithm = findAlgorithm(ctx);
 317  48
         String algorithmParams = findAlgorithmParams(ctx);
 318  48
         byte[] iv = findInitializationVector(ctx);
 319  
         
 320  48
         SecretKey macSecretKey = (SecretKey) getMacSecret(ctx);
 321  48
         String macAlgorithm = findMacAlgorithm(ctx);
 322  
                 
 323  
         try
 324  
         {
 325  
             // keep local to avoid threading issue
 326  48
             Mac mac = Mac.getInstance(macAlgorithm);
 327  48
             mac.init(macSecretKey);
 328  48
             Cipher cipher = Cipher.getInstance(algorithm + "/" + algorithmParams);
 329  48
             if (iv != null)
 330  
             {
 331  12
                 IvParameterSpec ivSpec = new IvParameterSpec(iv);
 332  12
                 cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
 333  12
             }
 334  
             else
 335  
             {
 336  36
                 cipher.init(Cipher.ENCRYPT_MODE, secretKey);
 337  
             }
 338  48
             if (log.isLoggable(Level.FINE))
 339  
             {
 340  0
                 log.fine("encrypting w/ " + algorithm + "/" + algorithmParams);
 341  
             }
 342  
             
 343  
             //EtM Composition Approach
 344  48
             int macLenght = mac.getMacLength();
 345  48
             byte[] secure = new byte[cipher.getOutputSize(insecure.length)+ macLenght];
 346  48
             int secureCount = cipher.doFinal(insecure,0,insecure.length,secure);
 347  48
             mac.update(secure, 0, secureCount);
 348  48
             mac.doFinal(secure, secureCount);
 349  
                         
 350  48
             return secure;
 351  
         }
 352  0
         catch (Exception e)
 353  
         {
 354  0
             throw new FacesException(e);
 355  
         }
 356  
     }
 357  
 
 358  
     public static final byte[] compress(byte[] bytes)
 359  
     {
 360  16
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
 361  
         try
 362  
         {
 363  16
             GZIPOutputStream gzip = new GZIPOutputStream(baos);
 364  16
             gzip.write(bytes, 0, bytes.length);
 365  16
             gzip.finish();
 366  16
             byte[] fewerBytes = baos.toByteArray();
 367  16
             gzip.close();
 368  16
             baos.close();
 369  16
             gzip = null;
 370  16
             baos = null;
 371  16
             return fewerBytes;
 372  
         }
 373  0
         catch (IOException e)
 374  
         {
 375  0
             throw new FacesException(e);
 376  
         }
 377  
     }
 378  
 
 379  
     public static final byte[] encode(byte[] bytes)
 380  
     {
 381  40
           return new Base64().encode(bytes);
 382  
     }
 383  
 
 384  
     /**
 385  
      * This fires during the Restore View phase, restoring state.
 386  
      */
 387  
     public static final Object reconstruct(String string, ExternalContext ctx)
 388  
     {
 389  
         byte[] bytes;
 390  
         try
 391  
         {
 392  24
             if(log.isLoggable(Level.FINE))
 393  
             {
 394  0
                 log.fine("Processing state : " + string);
 395  
             }
 396  
 
 397  24
             bytes = string.getBytes(ZIP_CHARSET);
 398  24
             bytes = decode(bytes);
 399  24
             if(isSecure(ctx))
 400  
             {
 401  24
                 bytes = decrypt(bytes, ctx);
 402  
             }
 403  16
             if( enableCompression(ctx) )
 404  
             {
 405  0
                 bytes = decompress(bytes);
 406  
             }
 407  16
             return getAsObject(bytes, ctx);
 408  
         }
 409  8
         catch (Throwable e)
 410  
         {
 411  8
             if (log.isLoggable(Level.FINE))
 412  
             {
 413  0
                 log.log(Level.FINE, "View State cannot be reconstructed", e);
 414  
             }
 415  8
             return null;
 416  
         }
 417  
     }
 418  
 
 419  
     public static final byte[] decode(byte[] bytes)
 420  
     {
 421  40
           return new Base64().decode(bytes);
 422  
     }
 423  
 
 424  
     public static final byte[] decompress(byte[] bytes)
 425  
     {
 426  16
         if(bytes == null)
 427  
         {
 428  0
             throw new NullPointerException("byte[] bytes");
 429  
         }
 430  
         
 431  16
         ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
 432  16
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
 433  16
         byte[] buffer = new byte[bytes.length];
 434  
         int length;
 435  
 
 436  
         try
 437  
         {
 438  16
             GZIPInputStream gis = new GZIPInputStream(bais);
 439  480
             while ((length = gis.read(buffer)) != -1)
 440  
             {
 441  472
                 baos.write(buffer, 0, length);
 442  
             }
 443  
 
 444  8
             byte[] moreBytes = baos.toByteArray();
 445  8
             baos.close();
 446  8
             bais.close();
 447  8
             gis.close();
 448  8
             baos = null;
 449  8
             bais = null;
 450  8
             gis = null;
 451  8
             return moreBytes;
 452  
         }
 453  8
         catch (IOException e)
 454  
         {
 455  8
             throw new FacesException(e);
 456  
         }
 457  
     }
 458  
     
 459  
     public static byte[] decrypt(byte[] secure, ExternalContext ctx)
 460  
     {
 461  51
         if (ctx == null)
 462  
         {
 463  0
             throw new NullPointerException("ExternalContext ctx");
 464  
         }
 465  
 
 466  51
         testConfiguration(ctx);
 467  
                 
 468  50
         SecretKey secretKey = (SecretKey) getSecret(ctx);
 469  48
         String algorithm = findAlgorithm(ctx);
 470  48
         String algorithmParams = findAlgorithmParams(ctx);
 471  48
         byte[] iv = findInitializationVector(ctx);
 472  
         
 473  48
         SecretKey macSecretKey = (SecretKey) getMacSecret(ctx);
 474  48
         String macAlgorithm = findMacAlgorithm(ctx);
 475  
 
 476  
         try
 477  
         {
 478  
             // keep local to avoid threading issue
 479  48
             Mac mac = Mac.getInstance(macAlgorithm);
 480  48
             mac.init(macSecretKey);
 481  48
             Cipher cipher = Cipher.getInstance(algorithm + "/"
 482  
                     + algorithmParams);
 483  48
             if (iv != null)
 484  
             {
 485  12
                 IvParameterSpec ivSpec = new IvParameterSpec(iv);
 486  12
                 cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);
 487  12
             }
 488  
             else
 489  
             {
 490  36
                 cipher.init(Cipher.DECRYPT_MODE, secretKey);
 491  
             }
 492  48
             if (log.isLoggable(Level.FINE))
 493  
             {
 494  0
                 log.fine("decrypting w/ " + algorithm + "/" + algorithmParams);
 495  
             }
 496  
 
 497  
             //EtM Composition Approach
 498  48
             int macLenght = mac.getMacLength();
 499  48
             mac.update(secure, 0, secure.length-macLenght);
 500  48
             byte[] signedDigestHash = mac.doFinal();
 501  
 
 502  48
             boolean isMacEqual = true;
 503  1008
             for (int i = 0; i < signedDigestHash.length; i++)
 504  
             {
 505  960
                 if (signedDigestHash[i] != secure[secure.length-macLenght+i])
 506  
                 {
 507  168
                     isMacEqual = false;
 508  
                     // MYFACES-2934 Must compare *ALL* bytes of the hash, 
 509  
                     // otherwise a side-channel timing attack is theorically possible
 510  
                     // but with a very very low probability, because the
 511  
                     // comparison time is too small to be measured compared to
 512  
                     // the overall request time and in real life applications,
 513  
                     // there are too many uncertainties involved.
 514  
                     //break;
 515  
                 }
 516  
             }
 517  48
             if (!isMacEqual)
 518  
             {
 519  16
                 throw new ViewExpiredException();
 520  
             }
 521  
             
 522  32
             return cipher.doFinal(secure, 0, secure.length-macLenght);
 523  
         }
 524  16
         catch (Exception e)
 525  
         {
 526  16
             throw new FacesException(e);
 527  
         }
 528  
     }
 529  
 
 530  
     /**
 531  
      * Performs deserialization with the serialization provider created from the
 532  
      * SerialFactory.
 533  
      * 
 534  
      * @param bytes
 535  
      * @param ctx
 536  
      * @return
 537  
      */
 538  
     
 539  
     public static final Object getAsObject(byte[] bytes, ExternalContext ctx)
 540  
     {
 541  32
         ByteArrayInputStream input = null;
 542  
 
 543  
         try
 544  
         {
 545  32
             input = new ByteArrayInputStream(bytes);
 546  
 
 547  
             // get the Factory that was instantiated @ startup
 548  32
             SerialFactory serialFactory = (SerialFactory) ctx.getApplicationMap().get(SERIAL_FACTORY);
 549  
             
 550  32
             if(serialFactory == null)
 551  
             {
 552  0
                 throw new NullPointerException("serialFactory");
 553  
             }
 554  
             
 555  32
             ObjectInputStream s = null;
 556  32
             Exception pendingException = null;
 557  
             try
 558  
             {
 559  32
                 s = serialFactory.getObjectInputStream(input); 
 560  24
                 Object object = null;
 561  24
                 if (System.getSecurityManager() != null)
 562  
                 {
 563  0
                     final ObjectInputStream ois = s;
 564  0
                     object = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>()
 565  0
                     {
 566  
                         //Put IOException and ClassNotFoundException as "checked" exceptions,
 567  
                         //so AccessController wrap them in a PrivilegedActionException
 568  
                         public Object run() throws PrivilegedActionException, 
 569  
                                                    IOException, ClassNotFoundException
 570  
                         {
 571  0
                             return ois.readObject();
 572  
                         }
 573  
                     });
 574  
                     // Since s has the same instance as ois,
 575  
                     // we don't need to close it here, rather
 576  
                     // close it on the finally block related to s
 577  
                     // and avoid duplicate close exceptions
 578  
                     // finally
 579  
                     // {
 580  
                     //    ois.close();
 581  
                     // }
 582  0
                 }
 583  
                 else
 584  
                 {
 585  24
                     object = s.readObject();
 586  
                 }
 587  24
                 return object;
 588  
             }
 589  8
             catch (Exception e)
 590  
             {
 591  8
                 pendingException = e;
 592  8
                 throw new FacesException(e);
 593  
             }
 594  
             finally
 595  
             {
 596  32
                 if (s != null)
 597  
                 {
 598  
                     try
 599  
                     {
 600  24
                         s.close();
 601  
                     }
 602  0
                     catch (IOException e)
 603  
                     {
 604  
                         // If a previous exception is thrown 
 605  
                         // ignore this, but if not, wrap it in a
 606  
                         // FacesException and throw it. In this way
 607  
                         // we preserve the original semantic of this
 608  
                         // method, but we handle correctly the case
 609  
                         // when we close a stream. Obviously, the 
 610  
                         // information about this exception is lost,
 611  
                         // but note that the interesting information 
 612  
                         // is always on pendingException, since we
 613  
                         // only do a readObject() on the outer try block.
 614  0
                         if (pendingException == null)
 615  
                         {
 616  0
                             throw new FacesException(e);
 617  
                         }                        
 618  
                     }
 619  
                     finally
 620  
                     {
 621  24
                         s = null;
 622  24
                     }
 623  
                 }
 624  
             }
 625  
         }
 626  
         finally
 627  
         {
 628  32
             if (input != null)
 629  
             {
 630  
                 try
 631  
                 {
 632  32
                     input.close();
 633  
                 }
 634  0
                 catch (IOException e)
 635  
                 {
 636  
                     //ignore it, because ByteArrayInputStream.close has
 637  
                     //no effect, but it is better to call close and preserve
 638  
                     //semantic from previous code.
 639  
                 }
 640  
                 finally
 641  
                 {
 642  32
                     input = null;
 643  32
                 }
 644  
             }
 645  
         }
 646  
     }
 647  
 
 648  
     /**
 649  
      * Utility method for generating base 64 encoded strings.
 650  
      * 
 651  
      * @param args
 652  
      * @throws UnsupportedEncodingException
 653  
      */
 654  
     public static void main (String[] args) throws UnsupportedEncodingException
 655  
     {
 656  0
         byte[] bytes = encode(args[0].getBytes(ZIP_CHARSET));
 657  0
           System.out.println(new String(bytes, ZIP_CHARSET));
 658  0
     }
 659  
 
 660  
     private static byte[] findInitializationVector(ExternalContext ctx)
 661  
     {
 662  
         
 663  96
         byte[] iv = null;
 664  96
         String ivString = ctx.getInitParameter(INIT_ALGORITHM_IV);
 665  
         
 666  96
         if(ivString == null)
 667  
         {
 668  72
             ivString = ctx.getInitParameter(INIT_ALGORITHM_IV.toLowerCase());
 669  
         }
 670  
         
 671  96
         if (ivString != null)
 672  
         {
 673  24
             iv = new Base64().decode(ivString.getBytes());
 674  
         }
 675  
         
 676  96
         return iv;
 677  
     }
 678  
 
 679  
     private static String findAlgorithmParams(ExternalContext ctx)
 680  
     {
 681  
         
 682  96
         String algorithmParams = ctx.getInitParameter(INIT_ALGORITHM_PARAM);
 683  
         
 684  96
         if (algorithmParams == null)
 685  
         {
 686  0
             algorithmParams = ctx.getInitParameter(INIT_ALGORITHM_PARAM.toLowerCase());
 687  
         }
 688  
         
 689  96
         if (algorithmParams == null)
 690  
         {
 691  0
             algorithmParams = DEFAULT_ALGORITHM_PARAMS;
 692  
         }
 693  
         
 694  96
         if (log.isLoggable(Level.FINE))
 695  
         {
 696  0
             log.fine("Using algorithm paramaters " + algorithmParams);
 697  
         }
 698  
         
 699  96
         return algorithmParams;
 700  
     }
 701  
 
 702  
     private static String findAlgorithm(ExternalContext ctx)
 703  
     {
 704  
         
 705  144
         String algorithm = ctx.getInitParameter(INIT_ALGORITHM);
 706  
         
 707  144
         if (algorithm == null)
 708  
         {
 709  0
             algorithm = ctx.getInitParameter(INIT_ALGORITHM.toLowerCase());
 710  
         }
 711  
 
 712  144
         return findAlgorithm( algorithm );
 713  
     }
 714  
     
 715  
     private static String findAlgorithm(ServletContext ctx)
 716  
     {
 717  
 
 718  46
         String algorithm = ctx.getInitParameter(INIT_ALGORITHM);
 719  
         
 720  46
         if (algorithm == null)
 721  
         {
 722  2
             algorithm = ctx.getInitParameter(INIT_ALGORITHM.toLowerCase());
 723  
         }
 724  
 
 725  46
         return findAlgorithm( algorithm );
 726  
     }
 727  
     
 728  
     private static String findAlgorithm(String initParam)
 729  
     {
 730  
         
 731  190
         if (initParam == null)
 732  
         {
 733  2
             initParam = DEFAULT_ALGORITHM;
 734  
         }
 735  
         
 736  190
         if (log.isLoggable(Level.FINE))
 737  
         {
 738  0
             log.fine("Using algorithm " + initParam);
 739  
         }
 740  
         
 741  190
         return initParam;
 742  
         
 743  
     }
 744  
 
 745  
     /**
 746  
      * Does nothing if the user has disabled the SecretKey cache. This is
 747  
      * useful when dealing with a JCA provider whose SecretKey 
 748  
      * implementation is not thread safe.
 749  
      * 
 750  
      * Instantiates a SecretKey instance based upon what the user has 
 751  
      * specified in the deployment descriptor.  The SecretKey is then 
 752  
      * stored in application scope where it can be used for all requests.
 753  
      */
 754  
     
 755  
     public static void initSecret(ServletContext ctx)
 756  
     {
 757  
         
 758  91
         if(ctx == null)
 759  
         {
 760  0
             throw new NullPointerException("ServletContext ctx");
 761  
         }
 762  
         
 763  91
         if (log.isLoggable(Level.FINE))
 764  
         {
 765  0
             log.fine("Storing SecretKey @ " + INIT_SECRET_KEY_CACHE);
 766  
         }
 767  
 
 768  
         // Create and store SecretKey on application scope
 769  91
         String cache = ctx.getInitParameter(INIT_SECRET_KEY_CACHE);
 770  
         
 771  91
         if(cache == null)
 772  
         {
 773  46
             cache = ctx.getInitParameter(INIT_SECRET_KEY_CACHE.toLowerCase());
 774  
         }
 775  
         
 776  91
         if (!"false".equals(cache))
 777  
         {
 778  46
             String algorithm = findAlgorithm(ctx);
 779  
             // you want to create this as few times as possible
 780  46
             ctx.setAttribute(INIT_SECRET_KEY_CACHE, new SecretKeySpec(
 781  
                     findSecret(ctx, algorithm), algorithm));
 782  
         }
 783  
 
 784  91
         if (log.isLoggable(Level.FINE))
 785  
         {
 786  0
             log.fine("Storing SecretKey @ " + INIT_MAC_SECRET_KEY_CACHE);
 787  
         }
 788  
         
 789  91
         String macCache = ctx.getInitParameter(INIT_MAC_SECRET_KEY_CACHE);
 790  
         
 791  91
         if(macCache == null)
 792  
         {
 793  91
             macCache = ctx.getInitParameter(INIT_MAC_SECRET_KEY_CACHE.toLowerCase());
 794  
         }
 795  
         
 796  91
         if (!"false".equals(macCache))
 797  
         {
 798  91
             String macAlgorithm = findMacAlgorithm(ctx);
 799  
             // init mac secret and algorithm 
 800  91
             ctx.setAttribute(INIT_MAC_SECRET_KEY_CACHE, new SecretKeySpec(
 801  
                     findMacSecret(ctx, macAlgorithm), macAlgorithm));
 802  
         }
 803  91
     }
 804  
     
 805  
     private static SecretKey getSecret(ExternalContext ctx)
 806  
     {
 807  100
         Object secretKey = (SecretKey) ctx.getApplicationMap().get(INIT_SECRET_KEY_CACHE);
 808  
         
 809  98
         if (secretKey == null)
 810  
         {
 811  50
             String cache = ctx.getInitParameter(INIT_SECRET_KEY_CACHE);
 812  
             
 813  50
             if(cache == null)
 814  
             {
 815  2
                 cache = ctx.getInitParameter(INIT_SECRET_KEY_CACHE.toLowerCase());
 816  
             }
 817  
             
 818  50
             if ("false".equals(cache))
 819  
             {
 820  
                 // No cache is used. This option is activated
 821  48
                 String secret = ctx.getInitParameter(INIT_SECRET);
 822  
                 
 823  48
                 if (secret == null)
 824  
                 {
 825  0
                     secret = ctx.getInitParameter(INIT_SECRET.toLowerCase());
 826  
                 }
 827  
 
 828  48
                 if (secret == null)
 829  
                 {
 830  0
                     throw new NullPointerException("Could not find secret using key '" + INIT_SECRET + "'");
 831  
                 }
 832  
                 
 833  48
                 String algorithm = findAlgorithm(ctx);
 834  
                 
 835  48
                 secretKey = new SecretKeySpec(findSecret(ctx, algorithm), algorithm);
 836  48
             }
 837  
             else
 838  
             {
 839  2
                 throw new NullPointerException("Could not find SecretKey in application scope using key '" 
 840  
                         + INIT_SECRET_KEY_CACHE + "'");
 841  
             }
 842  
         }
 843  
         
 844  96
         if( ! ( secretKey instanceof SecretKey ) )
 845  
         {
 846  0
             throw new ClassCastException("Did not find an instance of SecretKey "
 847  
                     + "in application scope using the key '" + INIT_SECRET_KEY_CACHE + "'");
 848  
         }
 849  
 
 850  
         
 851  96
         return (SecretKey) secretKey;
 852  
     }
 853  
 
 854  
     private static byte[] findSecret(ExternalContext ctx, String algorithm)
 855  
     {
 856  48
         String secret = ctx.getInitParameter(INIT_SECRET);
 857  
         
 858  48
         if (secret == null)
 859  
         {
 860  0
             secret = ctx.getInitParameter(INIT_SECRET.toLowerCase());
 861  
         }
 862  
         
 863  48
         return findSecret(secret, algorithm);
 864  
     }    
 865  
     
 866  
     private static byte[] findSecret(ServletContext ctx, String algorithm)
 867  
     {
 868  46
         String secret = ctx.getInitParameter(INIT_SECRET);
 869  
         
 870  46
         if (secret == null)
 871  
         {
 872  0
             secret = ctx.getInitParameter(INIT_SECRET.toLowerCase());
 873  
         }
 874  
         
 875  46
         return findSecret(secret, algorithm);
 876  
     }
 877  
     
 878  
     private static byte[] findSecret(String secret, String algorithm)
 879  
     {
 880  94
         byte[] bytes = null;
 881  
         
 882  94
         if(secret == null)
 883  
         {
 884  
             try
 885  
             {
 886  0
                 KeyGenerator kg = KeyGenerator.getInstance(algorithm);
 887  0
                 bytes = kg.generateKey().getEncoded();
 888  
                 
 889  0
                 if(log.isLoggable(Level.FINE))
 890  
                 {
 891  0
                     log.fine("generated random password of length " + bytes.length);
 892  
                 }
 893  
             }
 894  0
             catch (NoSuchAlgorithmException e)
 895  
             {
 896  
                 // Generate random password length 8, 
 897  0
                 int length = 8;
 898  0
                 bytes = new byte[length];
 899  0
                 new Random().nextBytes(bytes);
 900  
                 
 901  0
                 if(log.isLoggable(Level.FINE))
 902  
                 {
 903  0
                     log.fine("generated random password of length " + length);
 904  
                 }
 905  0
             }
 906  
         }
 907  
         else 
 908  
         {
 909  94
             bytes = new Base64().decode(secret.getBytes());
 910  
         }
 911  
         
 912  94
         return bytes;
 913  
     }
 914  
 
 915  
     private static String findMacAlgorithm(ExternalContext ctx)
 916  
     {
 917  
         
 918  96
         String algorithm = ctx.getInitParameter(INIT_MAC_ALGORITHM);
 919  
         
 920  96
         if (algorithm == null)
 921  
         {
 922  96
             algorithm = ctx.getInitParameter(INIT_MAC_ALGORITHM.toLowerCase());
 923  
         }
 924  
 
 925  96
         return findMacAlgorithm( algorithm );
 926  
 
 927  
     }
 928  
     
 929  
     private static String findMacAlgorithm(ServletContext ctx)
 930  
     {
 931  
 
 932  91
         String algorithm = ctx.getInitParameter(INIT_MAC_ALGORITHM);
 933  
         
 934  91
         if (algorithm == null)
 935  
         {
 936  91
             algorithm = ctx.getInitParameter(INIT_MAC_ALGORITHM.toLowerCase());
 937  
         }
 938  
 
 939  91
         return findMacAlgorithm( algorithm );
 940  
         
 941  
     }
 942  
     
 943  
     private static String findMacAlgorithm(String initParam)
 944  
     {
 945  
         
 946  187
         if (initParam == null)
 947  
         {
 948  187
             initParam = DEFAULT_MAC_ALGORITHM;
 949  
         }
 950  
         
 951  187
         if (log.isLoggable(Level.FINE))
 952  
         {
 953  0
             log.fine("Using algorithm " + initParam);
 954  
         }
 955  
         
 956  187
         return initParam;
 957  
         
 958  
     }
 959  
     
 960  
     private static SecretKey getMacSecret(ExternalContext ctx)
 961  
     {
 962  96
         Object secretKey = (SecretKey) ctx.getApplicationMap().get(INIT_MAC_SECRET_KEY_CACHE);
 963  
         
 964  96
         if (secretKey == null)
 965  
         {
 966  0
             String cache = ctx.getInitParameter(INIT_MAC_SECRET_KEY_CACHE);
 967  
             
 968  0
             if(cache == null)
 969  
             {
 970  0
                 cache = ctx.getInitParameter(INIT_MAC_SECRET_KEY_CACHE.toLowerCase());
 971  
             }
 972  
             
 973  0
             if ("false".equals(cache))
 974  
             {
 975  
                 // No cache is used. This option is activated
 976  0
                 String secret = ctx.getInitParameter(INIT_MAC_SECRET);
 977  
                 
 978  0
                 if (secret == null)
 979  
                 {
 980  0
                     secret = ctx.getInitParameter(INIT_MAC_SECRET.toLowerCase());
 981  
                 }
 982  
                 
 983  0
                 if (secret == null)
 984  
                 {
 985  0
                     throw new NullPointerException("Could not find secret using key '" + INIT_MAC_SECRET + "'");
 986  
                 }
 987  
                 
 988  0
                 String macAlgorithm = findMacAlgorithm(ctx);
 989  
 
 990  0
                 secretKey = new SecretKeySpec(findMacSecret(ctx, macAlgorithm), macAlgorithm);
 991  0
             }
 992  
             else
 993  
             {
 994  0
                 throw new NullPointerException("Could not find SecretKey in application scope using key '" 
 995  
                         + INIT_MAC_SECRET_KEY_CACHE + "'");
 996  
             }
 997  
         }
 998  
         
 999  96
         if( ! ( secretKey instanceof SecretKey ) )
 1000  
         {
 1001  0
             throw new ClassCastException("Did not find an instance of SecretKey "
 1002  
                     + "in application scope using the key '" + INIT_MAC_SECRET_KEY_CACHE + "'");
 1003  
         }
 1004  
 
 1005  
         
 1006  96
         return (SecretKey) secretKey;
 1007  
     }
 1008  
 
 1009  
     private static byte[] findMacSecret(ExternalContext ctx, String algorithm)
 1010  
     {
 1011  0
         String secret = ctx.getInitParameter(INIT_MAC_SECRET);
 1012  
         
 1013  0
         if (secret == null)
 1014  
         {
 1015  0
             secret = ctx.getInitParameter(INIT_MAC_SECRET.toLowerCase());
 1016  
         }
 1017  
  
 1018  0
         return findMacSecret(secret, algorithm);
 1019  
     }    
 1020  
     
 1021  
     private static byte[] findMacSecret(ServletContext ctx, String algorithm)
 1022  
     {
 1023  91
         String secret = ctx.getInitParameter(INIT_MAC_SECRET);
 1024  
         
 1025  91
         if (secret == null)
 1026  
         {
 1027  0
             secret = ctx.getInitParameter(INIT_MAC_SECRET.toLowerCase());
 1028  
         }
 1029  
         
 1030  91
         return findMacSecret(secret, algorithm);
 1031  
     }
 1032  
 
 1033  
     private static byte[] findMacSecret(String secret, String algorithm)
 1034  
     {
 1035  91
         byte[] bytes = null;
 1036  
         
 1037  91
         if(secret == null)
 1038  
         {
 1039  
             try
 1040  
             {
 1041  0
                 KeyGenerator kg = KeyGenerator.getInstance(algorithm);
 1042  0
                 bytes = kg.generateKey().getEncoded();
 1043  
                 
 1044  0
                 if(log.isLoggable(Level.FINE))
 1045  
                 {
 1046  0
                     log.fine("generated random mac password of length " + bytes.length);
 1047  
                 }
 1048  
             }
 1049  0
             catch (NoSuchAlgorithmException e)
 1050  
             {
 1051  
                 // Generate random password length 8, 
 1052  0
                 int length = 8;
 1053  0
                 bytes = new byte[length];
 1054  0
                 new Random().nextBytes(bytes);
 1055  
                 
 1056  0
                 if(log.isLoggable(Level.FINE))
 1057  
                 {
 1058  0
                     log.fine("generated random mac password of length " + length);
 1059  
                 }
 1060  0
             }
 1061  
         }
 1062  
         else 
 1063  
         {
 1064  91
             bytes = new Base64().decode(secret.getBytes());
 1065  
         }
 1066  
         
 1067  91
         return bytes;
 1068  
     }
 1069  
 }