1 /*
2 * Copyright 1997-2007 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any 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 }