Home » openjdk-7 » javax » crypto » [javadoc | source]

    1   /*
    2    * Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Oracle designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Oracle in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   22    * or visit www.oracle.com if you need additional information or have any
   23    * questions.
   24    */
   25   
   26   package javax.crypto;
   27   
   28   import java.io;
   29   import java.security.AlgorithmParameters;
   30   import java.security.Key;
   31   import java.security.InvalidKeyException;
   32   import java.security.InvalidAlgorithmParameterException;
   33   import java.security.NoSuchAlgorithmException;
   34   import java.security.NoSuchProviderException;
   35   
   36   /**
   37    * This class enables a programmer to create an object and protect its
   38    * confidentiality with a cryptographic algorithm.
   39    *
   40    * <p> Given any Serializable object, one can create a SealedObject
   41    * that encapsulates the original object, in serialized
   42    * format (i.e., a "deep copy"), and seals (encrypts) its serialized contents,
   43    * using a cryptographic algorithm such as DES, to protect its
   44    * confidentiality.  The encrypted content can later be decrypted (with
   45    * the corresponding algorithm using the correct decryption key) and
   46    * de-serialized, yielding the original object.
   47    *
   48    * <p> Note that the Cipher object must be fully initialized with the
   49    * correct algorithm, key, padding scheme, etc., before being applied
   50    * to a SealedObject.
   51    *
   52    * <p> The original object that was sealed can be recovered in two different
   53    * ways: <p>
   54    *
   55    * <ul>
   56    *
   57    * <li>by using the {@link #getObject(javax.crypto.Cipher) getObject}
   58    * method that takes a <code>Cipher</code> object.
   59    *
   60    * <p> This method requires a fully initialized <code>Cipher</code> object,
   61    * initialized with the
   62    * exact same algorithm, key, padding scheme, etc., that were used to seal the
   63    * object.
   64    *
   65    * <p> This approach has the advantage that the party who unseals the
   66    * sealed object does not require knowledge of the decryption key. For example,
   67    * after one party has initialized the cipher object with the required
   68    * decryption key, it could hand over the cipher object to
   69    * another party who then unseals the sealed object.
   70    *
   71    * <p>
   72    *
   73    * <li>by using one of the
   74    * {@link #getObject(java.security.Key) getObject} methods
   75    * that take a <code>Key</code> object.
   76    *
   77    * <p> In this approach, the <code>getObject</code> method creates a cipher
   78    * object for the appropriate decryption algorithm and initializes it with the
   79    * given decryption key and the algorithm parameters (if any) that were stored
   80    * in the sealed object.
   81    *
   82    * <p> This approach has the advantage that the party who
   83    * unseals the object does not need to keep track of the parameters (e.g., an
   84    * IV) that were used to seal the object.
   85    *
   86    * </ul>
   87    *
   88    * @author Li Gong
   89    * @author Jan Luehe
   90    * @see Cipher
   91    * @since 1.4
   92    */
   93   
   94   public class SealedObject implements Serializable {
   95   
   96       static final long serialVersionUID = 4482838265551344752L;
   97   
   98       /**
   99        * The serialized object contents in encrypted format.
  100        *
  101        * @serial
  102        */
  103       private byte[] encryptedContent = null;
  104   
  105       /**
  106        * The algorithm that was used to seal this object.
  107        *
  108        * @serial
  109        */
  110       private String sealAlg = null;
  111   
  112       /**
  113        * The algorithm of the parameters used.
  114        *
  115        * @serial
  116        */
  117       private String paramsAlg = null;
  118   
  119       /**
  120        * The cryptographic parameters used by the sealing Cipher,
  121        * encoded in the default format.
  122        * <p>
  123        * That is, <code>cipher.getParameters().getEncoded()</code>.
  124        *
  125        * @serial
  126        */
  127       protected byte[] encodedParams = null;
  128   
  129       /**
  130        * Constructs a SealedObject from any Serializable object.
  131        *
  132        * <p>The given object is serialized, and its serialized contents are
  133        * encrypted using the given Cipher, which must be fully initialized.
  134        *
  135        * <p>Any algorithm parameters that may be used in the encryption
  136        * operation are stored inside of the new <code>SealedObject</code>.
  137        *
  138        * @param object the object to be sealed; can be null.
  139        * @param c the cipher used to seal the object.
  140        *
  141        * @exception NullPointerException if the given cipher is null.
  142        * @exception IOException if an error occurs during serialization
  143        * @exception IllegalBlockSizeException if the given cipher is a block
  144        * cipher, no padding has been requested, and the total input length
  145        * (i.e., the length of the serialized object contents) is not a multiple
  146        * of the cipher's block size
  147        */
  148       public SealedObject(Serializable object, Cipher c) throws IOException,
  149           IllegalBlockSizeException
  150       {
  151           /*
  152            * Serialize the object
  153            */
  154   
  155           // creating a stream pipe-line, from a to b
  156           ByteArrayOutputStream b = new ByteArrayOutputStream();
  157           ObjectOutput a = new ObjectOutputStream(b);
  158           byte[] content;
  159           try {
  160               // write and flush the object content to byte array
  161               a.writeObject(object);
  162               a.flush();
  163               content = b.toByteArray();
  164           } finally {
  165               a.close();
  166           }
  167   
  168           /*
  169            * Seal the object
  170            */
  171           try {
  172               this.encryptedContent = c.doFinal(content);
  173           }
  174           catch (BadPaddingException ex) {
  175               // if sealing is encryption only
  176               // Should never happen??
  177           }
  178   
  179           // Save the parameters
  180           if (c.getParameters() != null) {
  181               this.encodedParams = c.getParameters().getEncoded();
  182               this.paramsAlg = c.getParameters().getAlgorithm();
  183           }
  184   
  185           // Save the encryption algorithm
  186           this.sealAlg = c.getAlgorithm();
  187       }
  188   
  189       /**
  190        * Constructs a SealedObject object from the passed-in SealedObject.
  191        *
  192        * @param so a SealedObject object
  193        * @exception NullPointerException if the given sealed object is null.
  194        */
  195       protected SealedObject(SealedObject so) {
  196           this.encryptedContent = (byte[]) so.encryptedContent.clone();
  197           this.sealAlg = so.sealAlg;
  198           this.paramsAlg = so.paramsAlg;
  199           if (so.encodedParams != null) {
  200               this.encodedParams = (byte[]) so.encodedParams.clone();
  201           } else {
  202               this.encodedParams = null;
  203           }
  204       }
  205   
  206       /**
  207        * Returns the algorithm that was used to seal this object.
  208        *
  209        * @return the algorithm that was used to seal this object.
  210        */
  211       public final String getAlgorithm() {
  212           return this.sealAlg;
  213       }
  214   
  215       /**
  216        * Retrieves the original (encapsulated) object.
  217        *
  218        * <p>This method creates a cipher for the algorithm that had been used in
  219        * the sealing operation.
  220        * If the default provider package provides an implementation of that
  221        * algorithm, an instance of Cipher containing that implementation is used.
  222        * If the algorithm is not available in the default package, other
  223        * packages are searched.
  224        * The Cipher object is initialized for decryption, using the given
  225        * <code>key</code> and the parameters (if any) that had been used in the
  226        * sealing operation.
  227        *
  228        * <p>The encapsulated object is unsealed and de-serialized, before it is
  229        * returned.
  230        *
  231        * @param key the key used to unseal the object.
  232        *
  233        * @return the original object.
  234        *
  235        * @exception IOException if an error occurs during de-serialiazation.
  236        * @exception ClassNotFoundException if an error occurs during
  237        * de-serialiazation.
  238        * @exception NoSuchAlgorithmException if the algorithm to unseal the
  239        * object is not available.
  240        * @exception InvalidKeyException if the given key cannot be used to unseal
  241        * the object (e.g., it has the wrong algorithm).
  242        * @exception NullPointerException if <code>key</code> is null.
  243        */
  244       public final Object getObject(Key key)
  245           throws IOException, ClassNotFoundException, NoSuchAlgorithmException,
  246               InvalidKeyException
  247       {
  248           if (key == null) {
  249               throw new NullPointerException("key is null");
  250           }
  251   
  252           try {
  253               return unseal(key, null);
  254           } catch (NoSuchProviderException nspe) {
  255               // we've already caught NoSuchProviderException's and converted
  256               // them into NoSuchAlgorithmException's with details about
  257               // the failing algorithm
  258               throw new NoSuchAlgorithmException("algorithm not found");
  259           } catch (IllegalBlockSizeException ibse) {
  260               throw new InvalidKeyException(ibse.getMessage());
  261           } catch (BadPaddingException bpe) {
  262               throw new InvalidKeyException(bpe.getMessage());
  263           }
  264       }
  265   
  266       /**
  267        * Retrieves the original (encapsulated) object.
  268        *
  269        * <p>The encapsulated object is unsealed (using the given Cipher,
  270        * assuming that the Cipher is already properly initialized) and
  271        * de-serialized, before it is returned.
  272        *
  273        * @param c the cipher used to unseal the object
  274        *
  275        * @return the original object.
  276        *
  277        * @exception NullPointerException if the given cipher is null.
  278        * @exception IOException if an error occurs during de-serialiazation
  279        * @exception ClassNotFoundException if an error occurs during
  280        * de-serialiazation
  281        * @exception IllegalBlockSizeException if the given cipher is a block
  282        * cipher, no padding has been requested, and the total input length is
  283        * not a multiple of the cipher's block size
  284        * @exception BadPaddingException if the given cipher has been
  285        * initialized for decryption, and padding has been specified, but
  286        * the input data does not have proper expected padding bytes
  287        */
  288       public final Object getObject(Cipher c)
  289           throws IOException, ClassNotFoundException, IllegalBlockSizeException,
  290               BadPaddingException
  291       {
  292           /*
  293            * Unseal the object
  294            */
  295           byte[] content = c.doFinal(this.encryptedContent);
  296   
  297           /*
  298            * De-serialize it
  299            */
  300           // creating a stream pipe-line, from b to a
  301           ByteArrayInputStream b = new ByteArrayInputStream(content);
  302           ObjectInput a = new extObjectInputStream(b);
  303           try {
  304               Object obj = a.readObject();
  305               return obj;
  306           } finally {
  307               a.close();
  308           }
  309       }
  310   
  311       /**
  312        * Retrieves the original (encapsulated) object.
  313        *
  314        * <p>This method creates a cipher for the algorithm that had been used in
  315        * the sealing operation, using an implementation of that algorithm from
  316        * the given <code>provider</code>.
  317        * The Cipher object is initialized for decryption, using the given
  318        * <code>key</code> and the parameters (if any) that had been used in the
  319        * sealing operation.
  320        *
  321        * <p>The encapsulated object is unsealed and de-serialized, before it is
  322        * returned.
  323        *
  324        * @param key the key used to unseal the object.
  325        * @param provider the name of the provider of the algorithm to unseal
  326        * the object.
  327        *
  328        * @return the original object.
  329        *
  330        * @exception IllegalArgumentException if the given provider is null
  331        * or empty.
  332        * @exception IOException if an error occurs during de-serialiazation.
  333        * @exception ClassNotFoundException if an error occurs during
  334        * de-serialiazation.
  335        * @exception NoSuchAlgorithmException if the algorithm to unseal the
  336        * object is not available.
  337        * @exception NoSuchProviderException if the given provider is not
  338        * configured.
  339        * @exception InvalidKeyException if the given key cannot be used to unseal
  340        * the object (e.g., it has the wrong algorithm).
  341        * @exception NullPointerException if <code>key</code> is null.
  342        */
  343       public final Object getObject(Key key, String provider)
  344           throws IOException, ClassNotFoundException, NoSuchAlgorithmException,
  345               NoSuchProviderException, InvalidKeyException
  346       {
  347           if (key == null) {
  348               throw new NullPointerException("key is null");
  349           }
  350           if (provider == null || provider.length() == 0) {
  351               throw new IllegalArgumentException("missing provider");
  352           }
  353   
  354           try {
  355               return unseal(key, provider);
  356           } catch (IllegalBlockSizeException ibse) {
  357               throw new InvalidKeyException(ibse.getMessage());
  358           } catch (BadPaddingException bpe) {
  359               throw new InvalidKeyException(bpe.getMessage());
  360           }
  361       }
  362   
  363   
  364       private Object unseal(Key key, String provider)
  365           throws IOException, ClassNotFoundException, NoSuchAlgorithmException,
  366               NoSuchProviderException, InvalidKeyException,
  367               IllegalBlockSizeException, BadPaddingException
  368       {
  369           /*
  370            * Create the parameter object.
  371            */
  372           AlgorithmParameters params = null;
  373           if (this.encodedParams != null) {
  374               try {
  375                   if (provider != null)
  376                       params = AlgorithmParameters.getInstance(this.paramsAlg,
  377                                                                provider);
  378                   else
  379                       params = AlgorithmParameters.getInstance(this.paramsAlg);
  380   
  381               } catch (NoSuchProviderException nspe) {
  382                   if (provider == null) {
  383                       throw new NoSuchAlgorithmException(this.paramsAlg
  384                                                          + " not found");
  385                   } else {
  386                       throw new NoSuchProviderException(nspe.getMessage());
  387                   }
  388               }
  389               params.init(this.encodedParams);
  390           }
  391   
  392           /*
  393            * Create and initialize the cipher.
  394            */
  395           Cipher c;
  396           try {
  397               if (provider != null)
  398                   c = Cipher.getInstance(this.sealAlg, provider);
  399               else
  400                   c = Cipher.getInstance(this.sealAlg);
  401           } catch (NoSuchPaddingException nspe) {
  402               throw new NoSuchAlgorithmException("Padding that was used in "
  403                                                  + "sealing operation not "
  404                                                  + "available");
  405           } catch (NoSuchProviderException nspe) {
  406               if (provider == null) {
  407                   throw new NoSuchAlgorithmException(this.sealAlg+" not found");
  408               } else {
  409                   throw new NoSuchProviderException(nspe.getMessage());
  410               }
  411           }
  412   
  413           try {
  414               if (params != null)
  415                   c.init(Cipher.DECRYPT_MODE, key, params);
  416               else
  417                   c.init(Cipher.DECRYPT_MODE, key);
  418           } catch (InvalidAlgorithmParameterException iape) {
  419               // this should never happen, because we use the exact same
  420               // parameters that were used in the sealing operation
  421               throw new RuntimeException(iape.getMessage());
  422           }
  423   
  424           /*
  425            * Unseal the object
  426            */
  427           byte[] content = c.doFinal(this.encryptedContent);
  428   
  429           /*
  430            * De-serialize it
  431            */
  432           // creating a stream pipe-line, from b to a
  433           ByteArrayInputStream b = new ByteArrayInputStream(content);
  434           ObjectInput a = new extObjectInputStream(b);
  435           try {
  436               Object obj = a.readObject();
  437               return obj;
  438           } finally {
  439               a.close();
  440           }
  441       }
  442   
  443       /**
  444        * Restores the state of the SealedObject from a stream.
  445        * @param s the object input stream.
  446        * @exception NullPointerException if s is null.
  447        */
  448       private void readObject(java.io.ObjectInputStream s)
  449           throws java.io.IOException, ClassNotFoundException
  450       {
  451           s.defaultReadObject();
  452           if (encryptedContent != null)
  453               encryptedContent = (byte[])encryptedContent.clone();
  454           if (encodedParams != null)
  455               encodedParams = (byte[])encodedParams.clone();
  456       }
  457   }
  458   
  459   final class extObjectInputStream extends ObjectInputStream {
  460   
  461       private static ClassLoader systemClassLoader = null;
  462   
  463       extObjectInputStream(InputStream in)
  464           throws IOException, StreamCorruptedException {
  465           super(in);
  466       }
  467   
  468       protected Class resolveClass(ObjectStreamClass v)
  469           throws IOException, ClassNotFoundException
  470       {
  471   
  472           try {
  473               /*
  474                * Calling the super.resolveClass() first
  475                * will let us pick up bug fixes in the super
  476                * class (e.g., 4171142).
  477                */
  478               return super.resolveClass(v);
  479           } catch (ClassNotFoundException cnfe) {
  480               /*
  481                * This is a workaround for bug 4224921.
  482                */
  483               ClassLoader loader = Thread.currentThread().getContextClassLoader();
  484               if (loader == null) {
  485                   if (systemClassLoader == null) {
  486                       systemClassLoader = ClassLoader.getSystemClassLoader();
  487                   }
  488                   loader = systemClassLoader;
  489                   if (loader == null) {
  490                       throw new ClassNotFoundException(v.getName());
  491                   }
  492               }
  493   
  494               return Class.forName(v.getName(), false, loader);
  495           }
  496       }
  497   }

Home » openjdk-7 » javax » crypto » [javadoc | source]