blob: 9cee4d070f1d206fed288c56cd3fa7d7bf0db854 [file] [log] [blame]
Aurimas Liutikas88c7ff12023-08-10 12:42:26 -07001/*
2 * Copyright (c) 1997, 2013, 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
26package javax.crypto;
27
28import java.io.*;
29import java.security.AlgorithmParameters;
30import java.security.Key;
31import java.security.InvalidKeyException;
32import java.security.InvalidAlgorithmParameterException;
33import java.security.NoSuchAlgorithmException;
34import 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:
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 * <li>by using one of the
72 * {@link #getObject(java.security.Key) getObject} methods
73 * that take a <code>Key</code> object.
74 *
75 * <p> In this approach, the <code>getObject</code> method creates a cipher
76 * object for the appropriate decryption algorithm and initializes it with the
77 * given decryption key and the algorithm parameters (if any) that were stored
78 * in the sealed object.
79 *
80 * <p> This approach has the advantage that the party who
81 * unseals the object does not need to keep track of the parameters (e.g., an
82 * IV) that were used to seal the object.
83 *
84 * </ul>
85 *
86 * @author Li Gong
87 * @author Jan Luehe
88 * @see Cipher
89 * @since 1.4
90 */
91
92public class SealedObject implements Serializable {
93
94 static final long serialVersionUID = 4482838265551344752L;
95
96 /**
97 * The serialized object contents in encrypted format.
98 *
99 * @serial
100 */
101 private byte[] encryptedContent = null;
102
103 /**
104 * The algorithm that was used to seal this object.
105 *
106 * @serial
107 */
108 private String sealAlg = null;
109
110 /**
111 * The algorithm of the parameters used.
112 *
113 * @serial
114 */
115 private String paramsAlg = null;
116
117 /**
118 * The cryptographic parameters used by the sealing Cipher,
119 * encoded in the default format.
120 * <p>
121 * That is, <code>cipher.getParameters().getEncoded()</code>.
122 *
123 * @serial
124 */
125 protected byte[] encodedParams = null;
126
127 /**
128 * Constructs a SealedObject from any Serializable object.
129 *
130 * <p>The given object is serialized, and its serialized contents are
131 * encrypted using the given Cipher, which must be fully initialized.
132 *
133 * <p>Any algorithm parameters that may be used in the encryption
134 * operation are stored inside of the new <code>SealedObject</code>.
135 *
136 * @param object the object to be sealed; can be null.
137 * @param c the cipher used to seal the object.
138 *
139 * @exception NullPointerException if the given cipher is null.
140 * @exception IOException if an error occurs during serialization
141 * @exception IllegalBlockSizeException if the given cipher is a block
142 * cipher, no padding has been requested, and the total input length
143 * (i.e., the length of the serialized object contents) is not a multiple
144 * of the cipher's block size
145 */
146 public SealedObject(Serializable object, Cipher c) throws IOException,
147 IllegalBlockSizeException
148 {
149 /*
150 * Serialize the object
151 */
152
153 // creating a stream pipe-line, from a to b
154 ByteArrayOutputStream b = new ByteArrayOutputStream();
155 ObjectOutput a = new ObjectOutputStream(b);
156 byte[] content;
157 try {
158 // write and flush the object content to byte array
159 a.writeObject(object);
160 a.flush();
161 content = b.toByteArray();
162 } finally {
163 a.close();
164 }
165
166 /*
167 * Seal the object
168 */
169 try {
170 this.encryptedContent = c.doFinal(content);
171 }
172 catch (BadPaddingException ex) {
173 // if sealing is encryption only
174 // Should never happen??
175 }
176
177 // Save the parameters
178 if (c.getParameters() != null) {
179 this.encodedParams = c.getParameters().getEncoded();
180 this.paramsAlg = c.getParameters().getAlgorithm();
181 }
182
183 // Save the encryption algorithm
184 this.sealAlg = c.getAlgorithm();
185 }
186
187 /**
188 * Constructs a SealedObject object from the passed-in SealedObject.
189 *
190 * @param so a SealedObject object
191 * @exception NullPointerException if the given sealed object is null.
192 */
193 protected SealedObject(SealedObject so) {
194 this.encryptedContent = so.encryptedContent.clone();
195 this.sealAlg = so.sealAlg;
196 this.paramsAlg = so.paramsAlg;
197 if (so.encodedParams != null) {
198 this.encodedParams = so.encodedParams.clone();
199 } else {
200 this.encodedParams = null;
201 }
202 }
203
204 /**
205 * Returns the algorithm that was used to seal this object.
206 *
207 * @return the algorithm that was used to seal this object.
208 */
209 public final String getAlgorithm() {
210 return this.sealAlg;
211 }
212
213 /**
214 * Retrieves the original (encapsulated) object.
215 *
216 * <p>This method creates a cipher for the algorithm that had been used in
217 * the sealing operation.
218 * If the default provider package provides an implementation of that
219 * algorithm, an instance of Cipher containing that implementation is used.
220 * If the algorithm is not available in the default package, other
221 * packages are searched.
222 * The Cipher object is initialized for decryption, using the given
223 * <code>key</code> and the parameters (if any) that had been used in the
224 * sealing operation.
225 *
226 * <p>The encapsulated object is unsealed and de-serialized, before it is
227 * returned.
228 *
229 * @param key the key used to unseal the object.
230 *
231 * @return the original object.
232 *
233 * @exception IOException if an error occurs during de-serialiazation.
234 * @exception ClassNotFoundException if an error occurs during
235 * de-serialiazation.
236 * @exception NoSuchAlgorithmException if the algorithm to unseal the
237 * object is not available.
238 * @exception InvalidKeyException if the given key cannot be used to unseal
239 * the object (e.g., it has the wrong algorithm).
240 * @exception NullPointerException if <code>key</code> is null.
241 */
242 public final Object getObject(Key key)
243 throws IOException, ClassNotFoundException, NoSuchAlgorithmException,
244 InvalidKeyException
245 {
246 if (key == null) {
247 throw new NullPointerException("key is null");
248 }
249
250 try {
251 return unseal(key, null);
252 } catch (NoSuchProviderException nspe) {
253 // we've already caught NoSuchProviderException's and converted
254 // them into NoSuchAlgorithmException's with details about
255 // the failing algorithm
256 throw new NoSuchAlgorithmException("algorithm not found");
257 } catch (IllegalBlockSizeException ibse) {
258 throw new InvalidKeyException(ibse.getMessage());
259 } catch (BadPaddingException bpe) {
260 throw new InvalidKeyException(bpe.getMessage());
261 }
262 }
263
264 /**
265 * Retrieves the original (encapsulated) object.
266 *
267 * <p>The encapsulated object is unsealed (using the given Cipher,
268 * assuming that the Cipher is already properly initialized) and
269 * de-serialized, before it is returned.
270 *
271 * @param c the cipher used to unseal the object
272 *
273 * @return the original object.
274 *
275 * @exception NullPointerException if the given cipher is null.
276 * @exception IOException if an error occurs during de-serialiazation
277 * @exception ClassNotFoundException if an error occurs during
278 * de-serialiazation
279 * @exception IllegalBlockSizeException if the given cipher is a block
280 * cipher, no padding has been requested, and the total input length is
281 * not a multiple of the cipher's block size
282 * @exception BadPaddingException if the given cipher has been
283 * initialized for decryption, and padding has been specified, but
284 * the input data does not have proper expected padding bytes
285 */
286 public final Object getObject(Cipher c)
287 throws IOException, ClassNotFoundException, IllegalBlockSizeException,
288 BadPaddingException
289 {
290 /*
291 * Unseal the object
292 */
293 byte[] content = c.doFinal(this.encryptedContent);
294
295 /*
296 * De-serialize it
297 */
298 // creating a stream pipe-line, from b to a
299 ByteArrayInputStream b = new ByteArrayInputStream(content);
300 ObjectInput a = new extObjectInputStream(b);
301 try {
302 Object obj = a.readObject();
303 return obj;
304 } finally {
305 a.close();
306 }
307 }
308
309 /**
310 * Retrieves the original (encapsulated) object.
311 *
312 * <p>This method creates a cipher for the algorithm that had been used in
313 * the sealing operation, using an implementation of that algorithm from
314 * the given <code>provider</code>.
315 * The Cipher object is initialized for decryption, using the given
316 * <code>key</code> and the parameters (if any) that had been used in the
317 * sealing operation.
318 *
319 * <p>The encapsulated object is unsealed and de-serialized, before it is
320 * returned.
321 *
322 * @param key the key used to unseal the object.
323 * @param provider the name of the provider of the algorithm to unseal
324 * the object.
325 *
326 * @return the original object.
327 *
328 * @exception IllegalArgumentException if the given provider is null
329 * or empty.
330 * @exception IOException if an error occurs during de-serialiazation.
331 * @exception ClassNotFoundException if an error occurs during
332 * de-serialiazation.
333 * @exception NoSuchAlgorithmException if the algorithm to unseal the
334 * object is not available.
335 * @exception NoSuchProviderException if the given provider is not
336 * configured.
337 * @exception InvalidKeyException if the given key cannot be used to unseal
338 * the object (e.g., it has the wrong algorithm).
339 * @exception NullPointerException if <code>key</code> is null.
340 */
341 public final Object getObject(Key key, String provider)
342 throws IOException, ClassNotFoundException, NoSuchAlgorithmException,
343 NoSuchProviderException, InvalidKeyException
344 {
345 if (key == null) {
346 throw new NullPointerException("key is null");
347 }
348 if (provider == null || provider.length() == 0) {
349 throw new IllegalArgumentException("missing provider");
350 }
351
352 try {
353 return unseal(key, provider);
354 } catch (IllegalBlockSizeException | BadPaddingException ex) {
355 throw new InvalidKeyException(ex.getMessage());
356 }
357 }
358
359
360 private Object unseal(Key key, String provider)
361 throws IOException, ClassNotFoundException, NoSuchAlgorithmException,
362 NoSuchProviderException, InvalidKeyException,
363 IllegalBlockSizeException, BadPaddingException
364 {
365 /*
366 * Create the parameter object.
367 */
368 AlgorithmParameters params = null;
369 if (this.encodedParams != null) {
370 try {
371 if (provider != null)
372 params = AlgorithmParameters.getInstance(this.paramsAlg,
373 provider);
374 else
375 params = AlgorithmParameters.getInstance(this.paramsAlg);
376
377 } catch (NoSuchProviderException nspe) {
378 if (provider == null) {
379 throw new NoSuchAlgorithmException(this.paramsAlg
380 + " not found");
381 } else {
382 throw new NoSuchProviderException(nspe.getMessage());
383 }
384 }
385 params.init(this.encodedParams);
386 }
387
388 /*
389 * Create and initialize the cipher.
390 */
391 Cipher c;
392 try {
393 if (provider != null)
394 c = Cipher.getInstance(this.sealAlg, provider);
395 else
396 c = Cipher.getInstance(this.sealAlg);
397 } catch (NoSuchPaddingException nspe) {
398 throw new NoSuchAlgorithmException("Padding that was used in "
399 + "sealing operation not "
400 + "available");
401 } catch (NoSuchProviderException nspe) {
402 if (provider == null) {
403 throw new NoSuchAlgorithmException(this.sealAlg+" not found");
404 } else {
405 throw new NoSuchProviderException(nspe.getMessage());
406 }
407 }
408
409 try {
410 if (params != null)
411 c.init(Cipher.DECRYPT_MODE, key, params);
412 else
413 c.init(Cipher.DECRYPT_MODE, key);
414 } catch (InvalidAlgorithmParameterException iape) {
415 // this should never happen, because we use the exact same
416 // parameters that were used in the sealing operation
417 throw new RuntimeException(iape.getMessage());
418 }
419
420 /*
421 * Unseal the object
422 */
423 byte[] content = c.doFinal(this.encryptedContent);
424
425 /*
426 * De-serialize it
427 */
428 // creating a stream pipe-line, from b to a
429 ByteArrayInputStream b = new ByteArrayInputStream(content);
430 ObjectInput a = new extObjectInputStream(b);
431 try {
432 Object obj = a.readObject();
433 return obj;
434 } finally {
435 a.close();
436 }
437 }
438
439 /**
440 * Restores the state of the SealedObject from a stream.
441 * @param s the object input stream.
442 * @exception NullPointerException if s is null.
443 */
444 private void readObject(java.io.ObjectInputStream s)
445 throws java.io.IOException, ClassNotFoundException
446 {
447 s.defaultReadObject();
448 if (encryptedContent != null)
449 encryptedContent = encryptedContent.clone();
450 if (encodedParams != null)
451 encodedParams = encodedParams.clone();
452 }
453}
454
455final class extObjectInputStream extends ObjectInputStream {
456
457 private static ClassLoader systemClassLoader = null;
458
459 extObjectInputStream(InputStream in)
460 throws IOException, StreamCorruptedException {
461 super(in);
462 }
463
464 protected Class<?> resolveClass(ObjectStreamClass v)
465 throws IOException, ClassNotFoundException
466 {
467
468 try {
469 /*
470 * Calling the super.resolveClass() first
471 * will let us pick up bug fixes in the super
472 * class (e.g., 4171142).
473 */
474 return super.resolveClass(v);
475 } catch (ClassNotFoundException cnfe) {
476 /*
477 * This is a workaround for bug 4224921.
478 */
479 ClassLoader loader = Thread.currentThread().getContextClassLoader();
480 if (loader == null) {
481 if (systemClassLoader == null) {
482 systemClassLoader = ClassLoader.getSystemClassLoader();
483 }
484 loader = systemClassLoader;
485 if (loader == null) {
486 throw new ClassNotFoundException(v.getName());
487 }
488 }
489
490 return Class.forName(v.getName(), false, loader);
491 }
492 }
493}