001/* SealedObject.java -- An encrypted Serializable object.
002   Copyright (C) 2004  Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038
039package javax.crypto;
040
041import java.io.ByteArrayInputStream;
042import java.io.ByteArrayOutputStream;
043import java.io.IOException;
044import java.io.ObjectInputStream;
045import java.io.ObjectOutputStream;
046import java.io.Serializable;
047
048import java.security.AlgorithmParameters;
049import java.security.InvalidAlgorithmParameterException;
050import java.security.InvalidKeyException;
051import java.security.Key;
052import java.security.NoSuchAlgorithmException;
053import 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 */
078public 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}