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 }