001    /* SealedObject.java -- An encrypted Serializable object.
002       Copyright (C) 2004  Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    
039    package javax.crypto;
040    
041    import java.io.ByteArrayInputStream;
042    import java.io.ByteArrayOutputStream;
043    import java.io.IOException;
044    import java.io.ObjectInputStream;
045    import java.io.ObjectOutputStream;
046    import java.io.Serializable;
047    
048    import java.security.AlgorithmParameters;
049    import java.security.InvalidAlgorithmParameterException;
050    import java.security.InvalidKeyException;
051    import java.security.Key;
052    import java.security.NoSuchAlgorithmException;
053    import java.security.NoSuchProviderException;
054    
055    /**
056     * This class allows any {@link java.io.Serializable} object to be
057     * stored in an encrypted form.
058     *
059     * <p>When the sealed object is ready to be unsealed (and deserialized)
060     * the caller may use either
061     *
062     * <ol>
063     * <li>{@link #getObject(javax.crypto.Cipher)}, which uses an
064     * already-initialized {@link javax.crypto.Cipher}.<br>
065     * <br>
066     * or,</li>
067     *
068     * <li>{@link #getObject(java.security.Key)} or {@link
069     * #getObject(java.security.Key,java.lang.String)}, which will
070     * initialize a new cipher instance with the {@link #encodedParams} that
071     * were stored with this sealed object (this is so parameters, such as
072     * the IV, don't need to be known by the one unsealing the object).</li>
073     * </ol>
074     *
075     * @author Casey Marshall (csm@gnu.org)
076     * @since 1.4
077     */
078    public class SealedObject implements Serializable
079    {
080    
081      // Constants and fields.
082      // ------------------------------------------------------------------------
083    
084      /** The encoded algorithm parameters. */
085      protected byte[] encodedParams;
086    
087      /** The serialized, encrypted object. */
088      private byte[] encryptedContent;
089    
090      /** The algorithm used to seal the object. */
091      private String sealAlg;
092    
093      /** The parameter type. */
094      private String paramsAlg;
095    
096      /** The cipher that decrypts when this object is unsealed. */
097      private transient Cipher sealCipher;
098    
099      /** Compatible with JDK1.4. */
100      private static final long serialVersionUID = 4482838265551344752L;
101    
102      // Constructors.
103      // ------------------------------------------------------------------------
104    
105      /**
106       * Create a new sealed object from a {@link java.io.Serializable}
107       * object and a cipher.
108       *
109       * @param object The object to seal.
110       * @param cipher The cipher to encrypt with.
111       * @throws java.io.IOException If serializing the object fails.
112       * @throws javax.crypto.IllegalBlockSizeException If the cipher has no
113       *         padding and the size of the serialized representation of the
114       *         object is not a multiple of the cipher's block size.
115       */
116      public SealedObject(Serializable object, Cipher cipher)
117        throws IOException, IllegalBlockSizeException
118      {
119        ByteArrayOutputStream baos = new ByteArrayOutputStream();
120        ObjectOutputStream oos = new ObjectOutputStream(baos);
121        oos.writeObject(object);
122        oos.flush();
123        try
124          {
125            encryptedContent = cipher.doFinal(baos.toByteArray());
126          }
127        catch (IllegalStateException ise)
128          {
129            throw new IOException("cipher not in proper state");
130          }
131        catch (BadPaddingException bpe)
132          {
133            throw new IOException(
134              "encrypting but got javax.crypto.BadPaddingException");
135          }
136        sealAlg = cipher.getAlgorithm();
137        encodedParams = cipher.getParameters().getEncoded();
138        paramsAlg = cipher.getParameters().getAlgorithm();
139      }
140    
141      /**
142       * Create a new sealed object from another sealed object.
143       *
144       * @param so The other sealed object.
145       */
146      protected SealedObject(SealedObject so)
147      {
148        this.encodedParams = (byte[]) so.encodedParams.clone();
149        this.encryptedContent = (byte[]) so.encryptedContent.clone();
150        this.sealAlg = so.sealAlg;
151        this.paramsAlg = so.paramsAlg;
152      }
153    
154      // Instance methods.
155      // ------------------------------------------------------------------------
156    
157      /**
158       * Get the name of the algorithm used to seal this object.
159       *
160       * @return The algorithm's name.
161       */
162      public final String getAlgorithm()
163      {
164        return sealAlg;
165      }
166    
167      /**
168       * Unseal and deserialize this sealed object with a specified (already
169       * initialized) cipher.
170       *
171       * @param cipher The cipher to decrypt with.
172       * @return The original object.
173       * @throws java.io.IOException If reading fails.
174       * @throws java.lang.ClassNotFoundException If deserialization fails.
175       * @throws javax.crypto.IllegalBlockSizeException If the cipher has no
176       *         padding and the encrypted data is not a multiple of the
177       *         cipher's block size.
178       * @throws javax.crypto.BadPaddingException If the padding bytes are
179       *         incorrect.
180       */
181      public final Object getObject(Cipher cipher)
182        throws IOException, ClassNotFoundException, IllegalBlockSizeException,
183               BadPaddingException
184      {
185        sealCipher = cipher;
186        return unseal();
187      }
188    
189      /**
190       * Unseal and deserialize this sealed object with the specified key.
191       *
192       * @param key The key to decrypt with.
193       * @return The original object.
194       * @throws java.io.IOException If reading fails.
195       * @throws java.lang.ClassNotFoundException If deserialization fails.
196       * @throws java.security.InvalidKeyException If the supplied key
197       *         cannot be used to unseal this object.
198       * @throws java.security.NoSuchAlgorithmException If the algorithm
199       *         used to originally seal this object is not available.
200       */
201      public final Object getObject(Key key)
202        throws IOException, ClassNotFoundException, InvalidKeyException,
203               NoSuchAlgorithmException
204      {
205        try
206          {
207            if (sealCipher == null)
208              sealCipher = Cipher.getInstance(sealAlg);
209          }
210        catch (NoSuchPaddingException nspe)
211          {
212            throw new NoSuchAlgorithmException(nspe.getMessage());
213          }
214        AlgorithmParameters params = null;
215        if (encodedParams != null)
216          {
217            params = AlgorithmParameters.getInstance(paramsAlg);
218            params.init(encodedParams);
219          }
220        try
221          {
222            sealCipher.init(Cipher.DECRYPT_MODE, key, params);
223            return unseal();
224          }
225        catch (InvalidAlgorithmParameterException iape)
226          {
227            throw new IOException("bad parameters");
228          }
229        catch (IllegalBlockSizeException ibse)
230          {
231            throw new IOException("illegal block size");
232          }
233        catch (BadPaddingException bpe)
234          {
235            throw new IOException("bad padding");
236          }
237      }
238    
239      /**
240       * Unseal and deserialize this sealed object with the specified key,
241       * using a cipher from the named provider.
242       *
243       * @param key      The key to decrypt with.
244       * @param provider The name of the provider to use.
245       * @return The original object.
246       * @throws java.io.IOException If reading fails.
247       * @throws java.lang.ClassNotFoundException If deserialization fails.
248       * @throws java.security.InvalidKeyException If the supplied key
249       *         cannot be used to unseal this object.
250       * @throws java.security.NoSuchAlgorithmException If the algorithm
251       *         used to originally seal this object is not available from
252       *         the named provider.
253       * @throws java.security.NoSuchProviderException If the named provider
254       *         does not exist.
255       */
256      public final Object getObject(Key key, String provider)
257        throws IOException, ClassNotFoundException, InvalidKeyException,
258               NoSuchAlgorithmException, NoSuchProviderException
259      {
260        try
261          {
262            sealCipher = Cipher.getInstance(sealAlg, provider);
263          }
264        catch (NoSuchPaddingException nspe)
265          {
266            throw new NoSuchAlgorithmException(nspe.getMessage());
267          }
268        AlgorithmParameters params = null;
269        if (encodedParams != null)
270          {
271            params = AlgorithmParameters.getInstance(paramsAlg, provider);
272            params.init(encodedParams);
273          }
274        try
275          {
276            sealCipher.init(Cipher.DECRYPT_MODE, key, params);
277            return unseal();
278          }
279        catch (InvalidAlgorithmParameterException iape)
280          {
281            throw new IOException("bad parameters");
282          }
283        catch (IllegalBlockSizeException ibse)
284          {
285            throw new IOException("illegal block size");
286          }
287        catch (BadPaddingException bpe)
288          {
289            throw new IOException("bad padding");
290          }
291      }
292    
293      // Own methods.
294      // ------------------------------------------------------------------------
295    
296      /**
297       * Deserialize this object.
298       *
299       * @param ois The input stream.
300       * @throws java.io.IOException If reading fails.
301       * @throws java.lang.ClassNotFoundException If reading fails.
302       */
303      private void readObject(ObjectInputStream ois)
304        throws IOException, ClassNotFoundException
305      {
306        encodedParams = (byte[]) ois.readObject();
307        encryptedContent = (byte[]) ois.readObject();
308        sealAlg = (String) ois.readObject();
309        paramsAlg = (String) ois.readObject();
310      }
311    
312      /**
313       * Serialize this object.
314       *
315       * @param oos The output stream.
316       * @throws java.io.IOException If writing fails.
317       */
318      private void writeObject(ObjectOutputStream oos)
319        throws IOException
320      {
321        oos.writeObject(encodedParams);
322        oos.writeObject(encryptedContent);
323        oos.writeObject(sealAlg);
324        oos.writeObject(paramsAlg);
325      }
326    
327      /**
328       * Unseal this object, returning it.
329       *
330       * @return The unsealed, deserialized Object.
331       * @throws java.io.IOException If reading fails.
332       * @throws java.io.ClassNotFoundException If reading fails.
333       * @throws javax.crypto.IllegalBlockSizeException If the cipher has no
334       *         padding and the encrypted data is not a multiple of the
335       *         cipher's block size.
336       * @throws javax.crypto.BadPaddingException If the padding bytes are
337       *         incorrect.
338       */
339      private Object unseal()
340        throws IOException, ClassNotFoundException, IllegalBlockSizeException,
341               BadPaddingException
342      {
343        ByteArrayInputStream bais = null;
344        try
345          {
346            bais = new ByteArrayInputStream(sealCipher.doFinal(encryptedContent));
347          }
348        catch (IllegalStateException ise)
349          {
350            throw new IOException("cipher not initialized");
351          }
352        ObjectInputStream ois = new ObjectInputStream(bais);
353        return ois.readObject();
354      }
355    }